merged in main from project spring-cloud-dataflow-apps-plugin

This commit is contained in:
Christian Tzolov
2023-06-13 12:38:10 +02:00
56 changed files with 6947 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
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
.vscode/

View File

@@ -0,0 +1 @@
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom

View File

@@ -0,0 +1 @@
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring

View 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* 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();
}
}

Binary file not shown.

View File

@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

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

View File

@@ -0,0 +1,17 @@
^\Q/*\E$
^\Q * Copyright \E20\d\d\-20\d\d\Q the original author or authors.\E$
^\Q *\E$
^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$
^\Q * you may not use this file except in compliance with the License.\E$
^\Q * You may obtain a copy of the License at\E$
^\Q *\E$
^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$
^\Q *\E$
^\Q * Unless required by applicable law or agreed to in writing, software\E$
^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$
^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$
^\Q * See the License for the specific language governing permissions and\E$
^\Q * limitations under the License.\E$
^\Q */\E$
^$
^.*$

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress files="(.*EmbeddedEngine\.java)" checks="[a-zA-Z0-9]*"/>
<suppress files="(.*Class\.java)" checks="[a-zA-Z0-9]*"/>
<suppress files="[\\/]src[\\/]main[\\/](protos)[\\/]"
checks="(Javadoc*|HideUtility*)"/>
<suppress files=".*sample.*" checks="(Javadoc*|HideUtility*)"/>
<suppress files=".*example.*" checks="(Javadoc*|HideUtility*)"/>
<suppress files=".*integration-tests.*" checks="(Javadoc*|HideUtility*)"/>
<suppress files="(.*MavenXmlWriter\.java)" checks="Javadoc*"/>
<suppress files="(.*Tests\.java|.*Tests\.groovy)" checks="Javadoc*"/>
<suppress files=".*src.test.java.*" checks="Javadoc*"/>
<suppress files="generated-sources" checks="[a-zA-Z0-9]*"/>
<suppress files=".*target.*" checks="[a-zA-Z0-9]*"/>
<suppress files=".*build.*" checks="[a-zA-Z0-9]*"/>
<suppress files=".*protos" checks="[a-zA-Z0-9]*"/>
</suppressions>

View File

@@ -0,0 +1,196 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"https://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="com.puppycrawl.tools.checkstyle.Checker">
<module name="SuppressionFilter">
<property name="file" value="${checkstyle.suppressions.file}"/>
<property name="optional" value="true"/>
</module>
<module name="SuppressionFilter">
<property name="file" value="${checkstyle.additional.suppressions.file}"/>
<property name="optional" value="true"/>
</module>
<!-- TAKEN FROM SPRING BOOT -->
<!-- Root Checks -->
<module name="com.puppycrawl.tools.checkstyle.checks.header.RegexpHeaderCheck">
<property name="headerFile" value="${checkstyle.header.file}"/>
<property name="fileExtensions" value="java,groovy"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck">
<property name="lineSeparator" value="lf"/>
</module>
<!-- TreeWalker Checks -->
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
<property name="tabWidth" value="1"/>
<!-- Spring Cloud ones -->
<!-- <module name="LineLength">
<property name="max" value="120"/>
</module> -->
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck">
<property name="illegalPkgs" value="com.google.common"/>
</module>
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="@checkstyle:off"/>
<property name="onCommentFormat" value="@checkstyle:on"/>
</module>
<!-- TAKEN FROM SPRING BOOT -->
<!-- Annotations -->
<module name="com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationUseStyleCheck">
<property name="elementStyle" value="compact"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.annotation.MissingOverrideCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.annotation.PackageAnnotationCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationLocationCheck">
<property name="allowSamelineSingleParameterlessAnnotation"
value="false"/>
</module>
<!-- Block Checks -->
<module name="com.puppycrawl.tools.checkstyle.checks.blocks.EmptyBlockCheck">
<property name="option" value="text"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheck">
<property name="option" value="alone"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.blocks.NeedBracesCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.blocks.AvoidNestedBlocksCheck"/>
<!-- Class Design -->
<module name="com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.design.InterfaceIsTypeCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.design.HideUtilityClassConstructorCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.design.MutableExceptionCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.design.InnerTypeLastCheck"/>
<!--<module name="com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck" />-->
<!-- Coding -->
<module name="com.puppycrawl.tools.checkstyle.checks.coding.CovariantEqualsCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.EmptyStatementCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.EqualsHashCodeCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.InnerAssignmentCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanExpressionCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.SimplifyBooleanReturnCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.StringLiteralEqualityCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.NestedForDepthCheck">
<property name="max" value="3"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.NestedIfDepthCheck">
<property name="max" value="3"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.NestedTryDepthCheck">
<property name="max" value="3"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.coding.MultipleVariableDeclarationsCheck"/>
<!--<module name="com.puppycrawl.tools.checkstyle.checks.coding.RequireThisCheck">
<property name="checkMethods" value="false" />
<property name="validateOnlyOverlapping" value="false" />
</module>-->
<module name="com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheck"/>
<!-- Imports -->
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck"/>
<!--<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStaticImportCheck">-->
<!--<property name="excludes"-->
<!--value="io.restassured.RestAssured.*, org.assertj.core.api.Assertions.*, org.assertj.core.api.Assumptions.*, org.awaitility.Awaitility.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.junit.jupiter.api.Assertions.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.ArgumentMatchers.*, org.mockito.Matchers.*, org.springframework.restdocs.headers.HeaderDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.payload.PayloadDocumentation.*, org.springframework.restdocs.request.RequestDocumentation.*, org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.*, org.springframework.restdocs.snippet.Attributes.*, org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.ExpectedCount.*, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*, org.springframework.test.web.servlet.result.MockMvcResultHandlers.*, org.springframework.web.reactive.function.BodyInserters.*, org.springframework.web.reactive.function.server.RequestPredicates.*, org.springframework.web.reactive.function.server.RouterFunctions.*, org.springframework.test.web.servlet.setup.MockMvcBuilders.*" />-->
<!--</module>-->
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck">
<property name="processJavadoc" value="true"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck">
<property name="groups" value="java,/^javax?\./,*,org.springframework"/>
<property name="ordered" value="true"/>
<property name="separated" value="true"/>
<property name="option" value="bottom"/>
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<!-- Javadoc Comments -->
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck">
<property name="scope" value="public"/>
<property name="excludeScope" value="package"/>
<property name="authorFormat" value=".+\s.+"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck">
<property name="scope" value="public"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocStyleCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.NonEmptyAtclauseDescriptionCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagContinuationIndentationCheck">
<property name="offset" value="0"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.AtclauseOrderCheck">
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
<property name="tagOrder"
value="@param, @author, @since, @see, @version, @serial, @deprecated"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.javadoc.AtclauseOrderCheck">
<property name="target" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
<property name="tagOrder"
value="@param, @return, @throws, @since, @deprecated, @see"/>
</module>
<!-- Miscellaneous -->
<module name="com.puppycrawl.tools.checkstyle.checks.indentation.CommentsIndentationCheck">
<property name="tokens" value="BLOCK_COMMENT_BEGIN"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.UpperEllCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.ArrayTypeStyleCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.OuterTypeFilenameCheck"/>
<!-- Modifiers -->
<module name="com.puppycrawl.tools.checkstyle.checks.modifier.RedundantModifierCheck"/>
<!-- Regexp -->
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="format" value="^\t* +\t*\S"/>
<property name="message"
value="Line has leading space characters; indentation should be performed with tabs only."/>
<property name="ignoreComments" value="true"/>
</module>
<!--<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="maximum" value="0"/>
<property name="format" value="org\.mockito\.Mockito\.(when|doThrow|doAnswer)" />
<property name="message"
value="Please use BDDMockito imports." />
<property name="ignoreComments" value="true" />
</module>-->
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">
<property name="maximum" value="0"/>
<property name="format"
value="org\.junit\.(Assert|jupiter\.api\.Assertions)\.assert"/>
<property name="message"
value="Please use AssertJ imports."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpCheck">
<property name="format" value="[ \t]+$"/>
<property name="illegalPattern" value="true"/>
<property name="message" value="Trailing whitespace"/>
</module>
<!-- Whitespace -->
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.MethodParamPadCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceAfterCheck">
<property name="tokens"
value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.NoWhitespaceBeforeCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.ParenPadCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.TypecastParenPadCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAfterCheck"/>
<module name="com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAroundCheck"/>
</module>
</module>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<!--
~ Copyright 2002-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.
-->
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"https://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- Configure checker to use UTF-8 encoding -->
<property name="charset" value="UTF-8"/>
<!-- Configure checker to run on files with these extensions -->
<property name="fileExtensions" value=""/>
<!-- For detailed checkstyle configuration, see https://github.com/spring-io/nohttp/tree/master/nohttp-checkstyle -->
<!--<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck">
</module>-->
</module>

310
spring-cloud-dataflow-apps-plugin/mvnw vendored Executable file
View File

@@ -0,0 +1,310 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

View File

@@ -0,0 +1,182 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@@ -0,0 +1,289 @@
<?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</groupId>
<artifactId>spring-cloud-dataflow-apps-plugin-parent</artifactId>
<version>1.0.8-SNAPSHOT</version>
<name>spring-cloud-dataflow-apps-plugin-parent</name>
<description>Spring Cloud Dataflow Apps Plugin Parent</description>
<packaging>pom</packaging>
<properties>
<java.version>17</java.version>
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
<maven-checkstyle-plugin.version>3.1.0</maven-checkstyle-plugin.version>
<disable.checks>false</disable.checks>
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
<maven-checkstyle-plugin.failOnViolation>true</maven-checkstyle-plugin.failOnViolation>
<maven-checkstyle-plugin.includeTestSourceDirectory>true</maven-checkstyle-plugin.includeTestSourceDirectory>
<puppycrawl-tools-checkstyle.version>8.29</puppycrawl-tools-checkstyle.version>
<checkstyle.location>https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow-apps-plugin/master/etc/checkstyle</checkstyle.location>
<checkstyle.suppressions.file>
${checkstyle.location}/checkstyle-suppressions.xml
</checkstyle.suppressions.file>
<checkstyle.nohttp.file>
${checkstyle.location}/nohttp-checkstyle.xml
</checkstyle.nohttp.file>
<checkstyle.additional.suppressions.file>
${checkstyle.location}/checkstyle-suppressions.xml
</checkstyle.additional.suppressions.file>
<nohttp-checkstyle.version>0.0.2.RELEASE</nohttp-checkstyle.version>
<disable.nohttp.checks>true</disable.nohttp.checks>
<spring-javaformat-checkstyle.version>0.0.7</spring-javaformat-checkstyle.version>
</properties>
<modules>
<module>spring-cloud-dataflow-apps-generator-plugin</module>
<module>spring-cloud-dataflow-apps-metadata-plugin</module>
<module>spring-cloud-dataflow-apps-docs-plugin</module>
</modules>
<url>https://github.com/spring-cloud/spring-cloud-dataflow-apps-plugin</url>
<developers>
<developer>
<id>scdf-apps-plugin-developers</id>
<name>SCDF Apps Plugin Developers</name>
<email>chackos at vmware com</email>
<organization>VMware</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
</developers>
<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/spring-cloud/spring-cloud-dataflow-apps-plugin.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-dataflow-apps-plugin.git</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-dataflow-apps-plugin</url>
</scm>
<build>
<plugins>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<executions>
<execution>
<id>javadoc</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<source>1.8</source>
<quiet>true</quiet>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<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.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${puppycrawl-tools-checkstyle.version}</version>
</dependency>
<dependency>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-checkstyle</artifactId>
<version>${spring-javaformat-checkstyle.version}</version>
</dependency>
<dependency>
<groupId>io.spring.nohttp</groupId>
<artifactId>nohttp-checkstyle</artifactId>
<version>${nohttp-checkstyle.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>checkstyle-validation</id>
<phase>validate</phase>
<inherited>true</inherited>
<configuration>
<skip>${disable.checks}</skip>
<configLocation>${checkstyle.location}/checkstyle.xml</configLocation>
<headerLocation>${checkstyle.location}/checkstyle-header.txt</headerLocation>
<propertyExpansion>
checkstyle.build.directory=${project.build.directory}
checkstyle.suppressions.file=${checkstyle.suppressions.file}
checkstyle.additional.suppressions.file=${checkstyle.additional.suppressions.file}
</propertyExpansion>
<consoleOutput>true</consoleOutput>
<includeTestSourceDirectory>
${maven-checkstyle-plugin.includeTestSourceDirectory}
</includeTestSourceDirectory>
<failsOnError>${maven-checkstyle-plugin.failsOnError}
</failsOnError>
<failOnViolation>
${maven-checkstyle-plugin.failOnViolation}
</failOnViolation>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
<execution>
<id>no-http-checkstyle-validation</id>
<phase>validate</phase>
<inherited>true</inherited>
<configuration>
<skip>${disable.nohttp.checks}</skip>
<configLocation>${checkstyle.nohttp.file}</configLocation>
<includes>**/*</includes>
<excludes>**/.idea/**/*,**/.git/**/*,**/target/**/*,**/*.log</excludes>
<sourceDirectories>./</sourceDirectories>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<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>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<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>
<version>1.6</version>
<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>
</project>

View File

@@ -0,0 +1,44 @@
= Spring Cloud Stream & Task Maven Documentation Plugin
Maven plugin for generating documentation for Spring Cloud stream and task app starters.
Assuming a Spring Cloud Stream / Task app (or app starter) has
specified selected configuration properties to be visible (i.e., providing src/main/resources/META-INF/dataflow-configuration-metadata.properties),
this plugin will help automate the documentation of such properties.
https://docs.spring.io/spring-cloud-dataflow/docs/1.1.0.M2/reference/html/spring-cloud-dataflow-register-apps.html#spring-cloud-dataflow-stream-app-whitelisting[whitelisted]
some configuration properties, this plugin will help automate the documentation of such properties.
== Usage
To use this plugin, simply add the following markers to your project `README.adoc` file:
```
//tag::configuration-properties[]
//end::configuration-properties[]
```
Then, configure this plugin for your app project (either directly or through a parent POM):
```
<build>
<plugins>
<plugin>
<groupId>org.springframework.cloud.stream.app.plugin</groupId>
<artifactId>spring-cloud-stream-app-documentation-maven-plugin</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<executions>
<execution>
<id>generate-documentation</id>
<phase>verify</phase>
<goals>
<goal>generate-documentation</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
Documentation for the visible properties shall appear on next build, which should be committed under VCS.

View File

@@ -0,0 +1,94 @@
<?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>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-plugin-parent</artifactId>
<version>1.0.8-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-dataflow-apps-docs-plugin</artifactId>
<packaging>maven-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-configuration-metadata</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.15.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,424 @@
/*
* Copyright 2016-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.dataflow.app.plugin;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.jar.Manifest;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.ExplodedArchive;
import org.springframework.boot.loader.archive.JarFileArchive;
import org.springframework.cloud.dataflow.configuration.metadata.BootApplicationConfigurationMetadataResolver;
import org.springframework.cloud.dataflow.configuration.metadata.BootClassLoaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* A maven plugin that will scan an asciidoc file for special comment markers and replace
* everything in between with a listing of visible configuration properties for a Spring
* Cloud Stream/Task app. Applications with multiple properties prefixes are grouped by default.
* This can be disabled by using {@code //tag::configuration-properties[group=false]}.
*
* @author Eric Bottard
* @author David Turanski
* @see <a href=
* "https://dataflow.spring.io/docs/feature-guides/general/application-metadata/#whitelisting-properties">Whitelisting
* Properties</a>
*/
@Mojo(name = "generate-documentation", requiresDependencyResolution = ResolutionScope.RUNTIME)
public class ConfigurationMetadataDocumentationMojo extends AbstractMojo {
static final String CONFIGURATION_PROPERTIES_START_TAG = "//tag::configuration-properties[";
static final String CONFIGURATION_PROPERTIES_END_TAG = "//end::configuration-properties[]";
private BootApplicationConfigurationMetadataResolver metadataResolver = new BootApplicationConfigurationMetadataResolver(
imageName -> null);
@Parameter(defaultValue = "${project}")
private MavenProject mavenProject;
@Parameter(defaultValue = "false")
private boolean failOnMissingDescription;
private boolean grouped = true;
public void execute() throws MojoExecutionException {
File readme = new File(mavenProject.getBasedir(), "README.adoc");
if (!readme.exists()) {
getLog().info(String.format("No README.adoc file found in %s, skipping", mavenProject.getBasedir()));
return;
}
Artifact artifact = mavenProject.getArtifact();
if (artifact.getFile() == null) {
getLog().info(String.format("Project in %s does not produce a build artifact, skipping",
mavenProject.getBasedir()));
return;
}
File tmp = new File(readme.getPath() + ".tmp");
try (PrintWriter out = new PrintWriter(tmp);
BufferedReader reader = new BufferedReader(new FileReader(readme))) {
String line;
do {
line = reader.readLine();
out.println(line);
}
while (line != null && !line.startsWith(CONFIGURATION_PROPERTIES_START_TAG));
if (line == null) {
getLog().info("No documentation section marker found");
return;
}
Map<String, String> startTagAttributes = startTagAttributes(line);
if ("false".equals(startTagAttributes.get("group"))) {
grouped = false;
}
ScatteredArchive archive = new ScatteredArchive(mavenProject);
BootClassLoaderFactory bootClassLoaderFactory = new BootClassLoaderFactory(archive, null);
try (URLClassLoader classLoader = bootClassLoaderFactory.createClassLoader()) {
debug(classLoader);
List<ConfigurationMetadataProperty> properties = metadataResolver.listProperties(archive, false);
Collections.sort(properties, Comparator.comparing(ConfigurationMetadataProperty::getId));
Map<String, List<ConfigurationMetadataProperty>> groupedProperties = groupProperties(properties);
grouped = grouped && groupedProperties.size() > 1;
if (grouped) {
out.println("Properties grouped by prefix:\n");
groupedProperties.forEach((group, props) -> {
getLog().debug(" Documenting group " + group);
out.println(asciidocForGroup(group));
listProperties(props, out, classLoader, prop -> prop.getName());
});
}
else {
listProperties(properties, out, classLoader, prop -> prop.getId());
}
do {
line = reader.readLine();
// drop lines
}
while (!line.startsWith(CONFIGURATION_PROPERTIES_END_TAG));
// Copy remaining lines, including //end::configuration-properties[]
while (line != null) {
out.println(line);
line = reader.readLine();
}
getLog().info(String.format("Documented %d configuration properties", properties.size()));
}
}
catch (Exception e) {
tmp.delete();
throw new MojoExecutionException("Error generating documentation", e);
}
try {
Files.move(tmp.toPath(), readme.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
catch (IOException e) {
throw new MojoExecutionException("Error moving tmp file to README.adoc", e);
}
}
private void listProperties(List<ConfigurationMetadataProperty> properties, PrintWriter out,
ClassLoader classLoader,
Function<ConfigurationMetadataProperty, String> propertyValue) {
for (ConfigurationMetadataProperty property : properties) {
getLog().debug("Documenting " + property.getId());
out.println(asciidocFor(property, classLoader, propertyValue));
}
}
private Map<String, List<ConfigurationMetadataProperty>> groupProperties(
List<ConfigurationMetadataProperty> properties) {
Map<String, List<ConfigurationMetadataProperty>> groupedProperties = new LinkedHashMap<>();
properties.forEach(property -> {
String group = group(property.getId());
if (!groupedProperties.containsKey(group)) {
groupedProperties.put(group, new LinkedList<>());
}
groupedProperties.get(group).add(property);
});
return groupedProperties;
}
private String group(String id) {
return id.lastIndexOf('.') > 0 ? id.substring(0, id.lastIndexOf('.')) : "";
}
private void debug(ClassLoader classLoader) {
if (classLoader instanceof URLClassLoader) {
List<URL> urls = Arrays.asList(((URLClassLoader) classLoader).getURLs());
getLog().debug("Classloader has the following URLs:\n" + urls.toString().replace(',', '\n'));
}
}
private String asciidocFor(ConfigurationMetadataProperty property, ClassLoader classLoader,
Function<ConfigurationMetadataProperty, String> propertyValue) {
return String.format("$$%s$$:: $$%s$$ *($$%s$$, default: `$$%s$$`%s)*",
propertyValue.apply(property),
niceDescription(property),
niceType(property),
niceDefault(property),
maybeHints(property, classLoader));
}
private String asciidocForGroup(String group) {
return "\n=== " + group + "\n";
}
private Map<String, String> startTagAttributes(String startTag) {
String attrs = startTag.substring(startTag.indexOf('[') + 1, startTag.indexOf(']'));
Set<String> set = StringUtils.commaDelimitedListToSet(attrs);
Map<String, String> attributes = new LinkedHashMap<>();
set.forEach(attr -> {
String[] keyValue = attr.split("=");
attributes.put(keyValue[0].trim(), keyValue.length == 2 ? keyValue[1].trim() : "");
});
return attributes;
}
private String niceDescription(ConfigurationMetadataProperty property) {
if (property.getDescription() == null) {
if (failOnMissingDescription) {
throw new RuntimeException("Missing description for property " + property.getId());
}
else {
return "<documentation missing>";
}
}
return property.getDescription();
}
private CharSequence maybeHints(ConfigurationMetadataProperty property, ClassLoader classLoader) {
String type = property.getType();
if (type == null) {
return "";
}
type = type.replace('$', '.');
if (ClassUtils.isPresent(type, classLoader)) {
Class<?> clazz = ClassUtils.resolveClassName(type, classLoader);
if (clazz.isEnum()) {
return ", possible values: `" + StringUtils.arrayToDelimitedString(clazz.getEnumConstants(), "`,`")
+ "`";
}
}
return "";
}
private String niceDefault(ConfigurationMetadataProperty property) {
if (property.getDefaultValue() == null) {
return "<none>";
}
else if ("".equals(property.getDefaultValue())) {
return "<empty string>";
}
else {
return stringify(property.getDefaultValue());
}
}
private String stringify(Object element) {
Class<?> clazz = element.getClass();
if (clazz == byte[].class) {
return Arrays.toString((byte[]) element);
}
else if (clazz == short[].class) {
return Arrays.toString((short[]) element);
}
else if (clazz == int[].class) {
return Arrays.toString((int[]) element);
}
else if (clazz == long[].class) {
return Arrays.toString((long[]) element);
}
else if (clazz == char[].class) {
return Arrays.toString((char[]) element);
}
else if (clazz == float[].class) {
return Arrays.toString((float[]) element);
}
else if (clazz == double[].class) {
return Arrays.toString((double[]) element);
}
else if (clazz == boolean[].class) {
return Arrays.toString((boolean[]) element);
}
else if (element instanceof Object[]) {
return Arrays.deepToString((Object[]) element);
}
else {
return element.toString();
}
}
private String niceType(ConfigurationMetadataProperty property) {
String type = property.getType();
if (type == null) {
return "<unknown>";
}
return niceType(type);
}
String niceType(String type) {
List<String> parts = new ArrayList<>();
int openBrackets = 0;
int lastGenericPart = 0;
for (int i = 0; i < type.length(); i++) {
switch (type.charAt(i)) {
case '<':
if (openBrackets++ == 0) {
parts.add(type.substring(0, i));
lastGenericPart = i + 1;
}
break;
case '>':
if (--openBrackets == 0) {
parts.add(type.substring(lastGenericPart, i));
}
break;
case ',':
if (openBrackets == 1) {
parts.add(type.substring(lastGenericPart, i));
lastGenericPart = i + 1;
}
break;
case ' ':
if (openBrackets == 1) {
lastGenericPart++;
}
break;
}
}
if (parts.isEmpty()) {
return unqualify(type); // simple type
}
else { // type with generics
StringBuilder sb = new StringBuilder(unqualify(parts.get(0)));
for (int i = 1; i < parts.size(); i++) {
if (i == 1) {
sb.append('<');
}
sb.append(unqualify(niceType(parts.get(i))));
if (i == parts.size() - 1) {
sb.append('>');
}
else {
sb.append(", ");
}
}
return sb.toString();
}
}
private String unqualify(String type) {
int lastDot = type.lastIndexOf('.');
int lastDollar = type.lastIndexOf('$');
return type.substring(Math.max(lastDot, lastDollar) + 1);
}
/**
* An adapter to boot {@link Archive} that satisfies just enough of the API to craft a
* ClassLoader that "sees" all the properties that this Mojo tries to document.
* @author Eric Bottard
*/
private static final class ScatteredArchive implements Archive {
private final MavenProject mavenProject;
private ScatteredArchive(MavenProject mavenProject) {
this.mavenProject = mavenProject;
}
@Override
public URL getUrl() throws MalformedURLException {
return mavenProject.getArtifact().getFile().toURI().toURL();
}
@Override
public Manifest getManifest() {
throw new UnsupportedOperationException();
}
@Override
public List<Archive> getNestedArchives(EntryFilter ignored) throws IOException {
try {
List<Archive> archives = new ArrayList<>(mavenProject.getRuntimeClasspathElements().size());
for (String dep : mavenProject.getRuntimeClasspathElements()) {
File file = new File(dep);
archives.add(file.isDirectory() ? new ExplodedArchive(file) : new JarFileArchive(file));
}
return archives;
}
catch (DependencyResolutionRequiredException e) {
throw new IOException("Could not create boot archive", e);
}
}
@Override
public Iterator<Entry> iterator() {
// BootClassLoaderFactory.createClassLoader (which uses this iterator call) is not
// actually
// used here. Returning the simples thing that works.
return Collections.emptyIterator();
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2017-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.dataflow.app.plugin;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigurationMetadataDocumentationMojoTests {
@Test
public void testJavaTypeBeautifier() {
ConfigurationMetadataDocumentationMojo mojo = new ConfigurationMetadataDocumentationMojo();
String s = mojo.niceType("java.lang.String");
assertThat(s).isEqualTo("String");
s = mojo.niceType("java.lang.Class<?>");
assertThat(s).isEqualTo("Class<?>");
s = mojo.niceType("java.util.Map$Entry<java.lang.String, java.util.Map<java.lang.Integer,java.util.List<java.lang.Long>>>");
assertThat(s).isEqualTo("Entry<String, Map<Integer, List<Long>>>");
}
}

View File

@@ -0,0 +1,302 @@
= spring-cloud-dataflow-apps-generator-plugin
Maven plugin for generating spring cloud stream applications: https://github.com/spring-cloud/stream-applications
It generates a separate maven project for every configured binder.
Plugin `Application` section helps to configure the application project independently form the Binders that
can be used. The `Binders` section helps to define the Binder specific configurations, that are merged to
the `Application` configurations to generate the binder specific maven projects.
The `Global` section (e.g. `global.application` and `global.binders`) helps to define configurations sharable
between many Applications and Binders.
As the following diagram illustrates, both the `Application` and the
`Binder` allow configuring various application and maven properties:
image::src/main/resources/doc/MojoConfiguration.jpg[]
== Build
mvn clean package [Requires JDK 8]
== Sample Configuration for generating spring cloud stream apps
In the parent POM define the `Application` and `Binder` configurations common for all applications to be generated.
Do this under the `pluginManagement` pom section and use the plugin's `global` configuration:
[source, xml]
----
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<executions>
<execution>
<id>app-gen</id>
<phase>package</phase>
<goals>
<goal>generate-app</goal>
</goals>
</execution>
</executions>
<configuration>
<generatedProjectHome>${basedir}/apps</generatedProjectHome>
<global>
<application>
<bootVersion>${spring-boot.version}</bootVersion>
<containerImage>
<enableMetadata>true</enableMetadata>
</containerImage>
<metadata>
<mavenPluginVersion>2.0.2.BUILD-SNAPSHOT</mavenPluginVersion>
</metadata>
<maven>
<dependencyManagement>
<dependency>
<groupId>org.springframework.cloud.fn</groupId>
<artifactId>function-dependencies</artifactId>
<version>${java-functions.version}</version>
</dependency>
<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>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>stream-applications-security-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>stream-applications-micrometer-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>stream-applications-postprocessor-common</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer.prometheus</groupId>
<artifactId>prometheus-rsocket-spring</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-wavefront</artifactId>
</dependency>
<dependency>
<groupId>io.pivotal.cfenv</groupId>
<artifactId>java-cfenv-boot</artifactId>
</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>
</dependencies>
</maven>
</application>
<binders>
<kafka>
<maven>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
</dependencies>
</maven>
</kafka>
<rabbit>
<maven>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
</dependencies>
</maven>
</rabbit>
</binders>
</global>
</configuration>
</plugin>
</plugins>
</pluginManagement>
----
Above global configurations will be reused by all children applications.
In addition, the child application can add application specific configurations, contributed to the inherited from
to the global:
[source, xml]
----
<plugins>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<application>
<name>syslog</name>
<type>source</type>
<version>${project.version}</version>
<configClass>org.springframework.cloud.fn.supplier.syslog.SyslogSupplierConfiguration.class</configClass>
<maven>
<dependencies>
<dependency>
<groupId>org.springframework.cloud.fn</groupId>
<artifactId>syslog-supplier</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>stream-applications-composite-function-support</artifactId>
<version>${stream-apps-core.version}</version>
</dependency>
</dependencies>
</maven>
</application>
</configuration>
</plugin>
...
</plugins>
----
== Configure Solace and Pubsub binders support
To generate applications for https://github.com/SolaceProducts/spring-cloud-stream-binder-solace[Solace] binders, extend your `binders` section with following configuration:
[source, xml]
----
<binders>
....
<solace>
<maven>
<dependencies>
<dependency>
<groupId>com.solace.spring.cloud</groupId>
<artifactId>spring-cloud-starter-stream-solace</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
</maven>
</solace>
....
</binders>
----
You can add it either to your application's POM or to the parent's `global` POM sections.
Similar to generate application for the https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-pubsub-stream-binder[PubSub]
binders and to your binders section configuration like this:
[source, xml]
----
<binders>
...
<pubsub>
<maven>
<dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-dependencies</artifactId>
<version>1.2.4.RELEASE</version>
</dependency>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-pubsub-stream-binder</artifactId>
</dependency>
</dependencies>
</maven>
</pubsub>
...
</binders>
----
== Custom Spring Boot Maven Plugin Configuration
Spring Boot Maven plugin offers a number of customization options by specifying a configuration section in the plugin definition.
For example, using the custom configuration, you can instruct the plugin to unpack a particular dependency.
You would do that using Spring Boot Maven plugin as below.
```
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
```
When using the app generator plugin, you can ask the plugin to include this information when it generates the necessary XML snippets for adding the Spring Boot Maven plugin.
Below is an example of doing so.
```
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<configuration>
<application>
...
<bootPluginConfiguration>
<![CDATA[
<requiresUnpack>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
</dependency>
</requiresUnpack>
]]>
</bootPluginConfiguration>
...
</application>
</configuration>
```
Pay attention to how the Spring Boot plugin configuration is wrapped inside a `CDATA` element.
This is necessary for the app generator to properly parse this information.

View File

@@ -0,0 +1,166 @@
<?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>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<packaging>maven-plugin</packaging>
<name>spring-cloud-dataflow-apps-generator-plugin</name>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-plugin-parent</artifactId>
<version>1.0.8-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.version>3.9.2</maven.version>
<maven.plugin.version>3.9.0</maven.plugin.version>
<jmustache.version>1.15</jmustache.version>
<spring.version>6.0.8</spring.version>
<commons.io.version>2.12.0</commons.io.version>
</properties>
<dependencies>
<dependency>
<groupId>com.samskivert</groupId>
<artifactId>jmustache</artifactId>
<version>${jmustache.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<scope>provided</scope>
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${maven.plugin.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${maven.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.15.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven.plugin.version}</version>
<configuration>
<goalPrefix>generate-app</goalPrefix>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<debug>true</debug>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<pomIncludes>
<pomInclude>*/pom.xml</pomInclude>
</pomIncludes>
<postBuildHookScript>verify</postBuildHookScript>
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
<settingsFile>src/it/settings.xml</settingsFile>
<goals>
<goal>clean</goal>
<goal>test-compile</goal>
</goals>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>install</goal>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2020-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.dataflow.app.plugin;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.pull.MXSerializer;
import org.codehaus.plexus.util.xml.pull.XmlSerializer;
/**
* Uses the private MavenXpp3Writer write methods to convert Model elements into XML strings.
*
* @author Christian Tzolov
*/
public final class MavenXmlWriter {
private MavenXmlWriter() {
}
/**
* Serializes any instance of a e.g. org.apache.maven.model.XXXX class into XML text.
* Via reflections calls the private MavenXpp3Writer#writeXXXX(XXXX, String, XmlSerializer) method.
* As xml tag uses the lower case of the XXXX class name (e.g. xxxx).
* <p>
* If above name conventions don't hold or additional processing is required implement a dedicated overload method.
* For example see {@link #toXml(Plugin)}
*
* @param element instance of a org.apache.maven.model.* class.
* @param <T> element type.
* @return XML text serialization of the element.
*/
public static <T> String toXml(T element) {
String privateMethodName = "write" + element.getClass().getSimpleName();
String xmlTagName = element.getClass().getSimpleName().toLowerCase();
return write(serializer -> invokeMavenXppWriteMethod(element, privateMethodName, xmlTagName, serializer));
}
/**
* Serializes an {@link Plugin} instance into XML text, using reflection to invoke the private
* MavenXpp3Writer#writePlugin(Plugin, String, XmlSerializer) method. The Plugin Configuration block (when present)
* requires special handling to convert String values into Xpp3Dom object before writing.
* <p>
* For Plugin Configuration you must nest the configuration content into a CDATA block like shown here:
* <pre>{@code
* <additionalPlugins>
* <plugin>
* <groupId>com.google.cloud.tools</groupId>
* <artifactId>jib-maven-plugin</artifactId>
* <version>3.3.0</version>
* <configuration>
* <![CDATA[
* <from><image>springcloud/baseimage:1.0.4</image></from>
* <to>
* <image>springcloudstream:${project.artifactId}</image>
* <tags><tag>latest</tag></tags>
* </to>
* <container>
* <format>Docker</format>
* </container>
* ]]>
* </configuration>
* </plugin>
* </additionalPlugins>
* }</pre>
*
* @param plugin Plugin instance to serialize.
* @return POM/XML serialization of the plugin.
*/
public static String toXml(Plugin plugin) {
try {
Object configuration = plugin.getConfiguration();
if (configuration != null && !(configuration instanceof Xpp3Dom)) {
plugin.setConfiguration(Xpp3DomBuilder.build(new StringReader(
"<configuration>" + configuration.toString() + "</configuration>")));
}
}
catch (Exception ex) {
throw new IllegalStateException("Issue creating config for additional plugin", ex);
}
return write(serializer -> invokeMavenXppWriteMethod(
plugin, "writePlugin", "plugin", serializer));
}
/**
* Template method that helps generating well-formed and valid XML segments. It uses the {@link XmlSerializer}
* to create the create the XML structure and delegates to the #elementWriter callback to fill in the content.
*
* @param elementWriter Callback handler that contributes the XML content.
* @return Returns well-formed XML element.
*/
public static String write(Consumer<XmlSerializer> elementWriter) {
try {
Writer writer = new StringWriter();
XmlSerializer serializer = new MXSerializer();
serializer.setProperty("http://xmlpull.org/v1/doc/properties.html#serializer-indentation", " ");
serializer.setProperty("http://xmlpull.org/v1/doc/properties.html#serializer-line-separator", "\n");
serializer.setOutput(writer);
serializer.startDocument("UTF-8", null);
elementWriter.accept(serializer);
serializer.endDocument();
String result = writer.toString();
return result.substring(result.indexOf('\n') + 1); // remove first line (e.g. remove the <?xml ... ?>)
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Uses the private MavenXpp3Writer methods to convert Model elements into XML strings.
*
* @param modelElementToWrite a parameterizable org.apache.maven.model.T instance to serialize.
* @param <T> The actual org.apache.maven.model's class type.
* @param writeMethodName Name of the private {@link MavenXpp3Writer} method to call.
* @param xmlTagName Name for the XML tag to surround the serialized model instance.
* @param serializer The XmlSerializer used to write the xml.
*/
public static <T> void invokeMavenXppWriteMethod(T modelElementToWrite, String writeMethodName,
String xmlTagName, XmlSerializer serializer) {
try {
MavenXpp3Writer pomWriter = new MavenXpp3Writer();
Method method = pomWriter.getClass().getDeclaredMethod(
writeMethodName, modelElementToWrite.getClass(), String.class, XmlSerializer.class);
method.setAccessible(true); // allow invoking private method.
method.invoke(pomWriter, modelElementToWrite, xmlTagName, serializer);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Indent the input string by the provided indentation steps.
* @param input String to indent.
* @param indentation Number of indentation steps.
* @return Returns the formatted string.
*/
public static String indent(String input, int indentation) {
String indentPrefix = "\n" + IntStream.range(0, indentation).mapToObj(i -> " ").collect(Collectors.joining());
String indentedInput = input.replace("\n", indentPrefix);
return indentedInput.substring(0, indentedInput.lastIndexOf(indentPrefix)); // remove the last empty line.
}
}

View File

@@ -0,0 +1,780 @@
/*
* Copyright 2020-2022 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.dataflow.app.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Repository;
import org.apache.maven.model.RepositoryBase;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.springframework.cloud.dataflow.app.plugin.generator.AppDefinition;
import org.springframework.cloud.dataflow.app.plugin.generator.BinderDefinition;
import org.springframework.cloud.dataflow.app.plugin.generator.ProjectGenerator;
import org.springframework.cloud.dataflow.app.plugin.generator.ProjectGeneratorProperties;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* @author Christian Tzolov
* @author David Turanski
* @author Soby Chacko
*/
@Mojo(name = "generate-app")
public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo {
private static final String DEPRECATED_WHITELIST_FILE_NAME = "dataflow-configuration-metadata-whitelist.properties";
private static final String VISIBLE_PROPERTIES_FILE_NAME = "dataflow-configuration-metadata.properties";
private static final String CONFIGURATION_PROPERTIES_CLASSES = "configuration-properties.classes";
private static final String CONFIGURATION_PROPERTIES_NAMES = "configuration-properties.names";
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@Parameter(defaultValue = "${project.resources[0].directory}", readonly = true, required = true)
private File projectResourcesDir;
@Parameter(defaultValue = "./apps", required = true)
private String generatedProjectHome;
@Parameter
private Global global = new Global();
@Parameter(required = true)
private Application application;
@Parameter
private Map<String, Binder> binders = new HashMap<>();
@Override
public void execute() throws MojoFailureException, MojoExecutionException {
// ----------------------------------------------------------------------------------------------------------
// Application Configuration
// ----------------------------------------------------------------------------------------------------------
AppDefinition app = new AppDefinition();
String bootVersion = StringUtils.isEmpty(this.application.getBootVersion()) ?
this.global.getApplication().getBootVersion() : this.application.getBootVersion();
if (StringUtils.isEmpty(bootVersion)) {
throw new MojoExecutionException("The application.bootVersion parameter is required!");
}
app.setSpringBootVersion(bootVersion);
String applicationName = StringUtils.isEmpty(this.application.getName()) ?
this.global.getApplication().getName() : this.application.getName();
if (StringUtils.isEmpty(applicationName)) {
throw new MojoExecutionException("The application.name parameter is required!");
}
app.setName(applicationName);
AppDefinition.AppType applicationType = (this.application.getType() == null) ?
this.global.getApplication().getType() : this.application.getType();
if (applicationType == null) {
throw new MojoExecutionException("The application.type parameter is required!");
}
app.setType(applicationType);
String applicationVersion = StringUtils.isEmpty(this.application.getVersion()) ?
this.global.getApplication().getVersion() : this.application.getVersion();
if (StringUtils.isEmpty(applicationVersion)) {
throw new MojoExecutionException("The application.version parameter is required!");
}
app.setVersion(applicationVersion);
String applicationConfigClass = StringUtils.isEmpty(this.application.getConfigClass()) ?
this.global.getApplication().getConfigClass() : this.application.getConfigClass();
app.setConfigClass(applicationConfigClass); // TODO is applicationConfigClass a required parameter?
String applicationGroupId = StringUtils.isEmpty(this.application.getGroupId()) ?
this.global.getApplication().getGroupId() : this.application.getGroupId();
app.setGroupId(applicationGroupId); // optional parameter
String applicationFunctionDefinition = StringUtils.isEmpty(this.application.getFunctionDefinition()) ?
this.global.getApplication().getFunctionDefinition() : this.application.getFunctionDefinition();
app.setFunctionDefinition(applicationFunctionDefinition); //TODO is applicationFunctionDefinition required?
String metadataMavenPluginVersion = StringUtils.isEmpty(this.application.getMetadata().getMavenPluginVersion()) ?
this.global.getApplication().getMetadata().getMavenPluginVersion() : this.application.getMetadata().getMavenPluginVersion();
if (StringUtils.isEmpty(metadataMavenPluginVersion)) {
throw new MojoExecutionException("The application.metadata.mavenPluginVersion parameter is required!");
}
app.getMetadata().setMavenPluginVersion(metadataMavenPluginVersion);
List<String> allSourceTypeFilters = new ArrayList<>(this.global.getApplication().getMetadata().getSourceTypeFilters());
allSourceTypeFilters.addAll(this.application.getMetadata().getSourceTypeFilters());
List<String> allNamedFilters = new ArrayList<>(this.global.getApplication().getMetadata().getNameFilters());
allNamedFilters.addAll(this.application.getMetadata().getNameFilters());
this.populateVisiblePropertiesFromFile(allSourceTypeFilters, allNamedFilters);
if (!CollectionUtils.isEmpty(allSourceTypeFilters)) {
app.getMetadata().setSourceTypeFilters(allSourceTypeFilters);
}
if (!CollectionUtils.isEmpty(allNamedFilters)) {
app.getMetadata().setNameFilters(allNamedFilters);
}
// Application properties
Map<String, String> allApplicationProperties = new TreeMap<>(this.global.getApplication().getProperties());
allApplicationProperties.putAll(this.application.getProperties());
app.setProperties(allApplicationProperties.entrySet().stream()
.map(e -> e.getKey() + "=" + (StringUtils.hasText(e.getValue()) ?
e.getValue().replaceAll("^\"|\"$", "") : ""))
.collect(Collectors.toList()));
// Maven properties
Map<String, String> allMavenProperties = new TreeMap<>(this.global.getApplication().getMaven().getProperties());
allMavenProperties.putAll(this.application.getMaven().getProperties());
app.getMaven().setProperties(allMavenProperties.entrySet().stream()
.map(es -> "<" + es.getKey() + ">" + (StringUtils.hasText(es.getValue()) ?
es.getValue().replaceAll("^\"|\"$", "") : "") + "</" + es.getKey() + ">")
.collect(Collectors.toList()));
//Maven BOM. For DependencyManagement it is important to retain the exact definition order!
// Override the global dependencies with matching app dependency definitions. Retain the definition order!
List<Dependency> allDeps = this.global.getApplication().getMaven().getDependencyManagement().stream()
.map(globalDep -> this.application.getMaven().getDependencyManagement().stream()
.filter(d -> isSameArtifact(globalDep, d))
.findFirst().orElse(globalDep))
.collect(Collectors.toList());
// Add remaining app dependencies that have not be used for overriding. Retain the order!
this.application.getMaven().getDependencyManagement()
.forEach(appDep -> {
if (allDeps.stream().noneMatch(d -> isSameArtifact(appDep, d))) {
allDeps.add(appDep);
}
});
app.getMaven().setDependencyManagement(allDeps.stream()
.filter(Objects::nonNull)
.peek(dependency -> {
dependency.setScope("import");
dependency.setType("pom");
})
.map(MavenXmlWriter::toXml)
.map(xml -> MavenXmlWriter.indent(xml, 12))
.collect(Collectors.toList()));
//Maven Dependencies
Map<String, Dependency> allDependenciesMap = this.global.getApplication().getMaven().getDependencies().stream()
.collect(Collectors.toMap(d -> d.getGroupId() + ":" + d.getArtifactId(), d -> d));
// Ensure that for dependencies with same maven coordinates that application definition overrides the global one.
allDependenciesMap.putAll(this.application.getMaven().getDependencies().stream()
.collect(Collectors.toMap(d -> d.getGroupId() + ":" + d.getArtifactId(), d -> d)));
app.getMaven().setDependencies(allDependenciesMap.values().stream()
.map(MavenXmlWriter::toXml)
.map(xml -> MavenXmlWriter.indent(xml, 8))
.collect(Collectors.toList()));
//Maven Plugins
Map<String, Plugin> allPluginsMap = this.global.getApplication().getMaven().getPlugins().stream()
.collect(Collectors.toMap(p -> p.getGroupId() + ":" + p.getArtifactId(), p -> p));
// Ensure that for plugins with same maven coordinates that application definition overrides the global one.
allPluginsMap.putAll(this.application.getMaven().getPlugins().stream()
.collect(Collectors.toMap(p -> p.getGroupId() + ":" + p.getArtifactId(), p -> p)));
app.getMaven().setPlugins(allPluginsMap.values().stream()
.map(MavenXmlWriter::toXml)
.map(d -> MavenXmlWriter.indent(d, 12))
.collect(Collectors.toList()));
// Maven Repositories
Map<String, Repository> allRepositoriesMap = this.global.getApplication().getMaven().getRepositories().stream()
.collect(Collectors.toMap(RepositoryBase::getId, r -> r));
// Ensure that for repos with same maven coordinates that application definition overrides the global one.
allRepositoriesMap.putAll(this.application.getMaven().getRepositories().stream()
.collect(Collectors.toMap(RepositoryBase::getId, r -> r)));
app.getMaven().setRepositories(allRepositoriesMap.values().stream()
.map(MavenXmlWriter::toXml)
.map(d -> MavenXmlWriter.indent(d, 12))
.collect(Collectors.toList()));
// Container Image configuration
AppDefinition.ContainerImageFormat containerImageFormat = (this.application.getContainerImage().getFormat() != null) ?
this.application.getContainerImage().getFormat() : this.global.getApplication().getContainerImage().getFormat();
containerImageFormat = (containerImageFormat == null) ? AppDefinition.ContainerImageFormat.Docker : containerImageFormat;
app.getContainerImage().setFormat(containerImageFormat);
// TODO how to choose between global an app metadata enabling?
app.getContainerImage().setEnableMetadata(this.application.getContainerImage().isEnableMetadata());
if (StringUtils.hasText(this.application.getContainerImage().getOrgName())) {
app.getContainerImage().setOrgName(this.application.getContainerImage().getOrgName());
}
else if (StringUtils.hasText(this.global.getApplication().getContainerImage().getOrgName())) {
app.getContainerImage().setOrgName(this.global.getApplication().getContainerImage().getOrgName());
}
app.getContainerImage().setTag(applicationVersion);
if (StringUtils.hasText(this.application.getContainerImage().getBaseImage())) {
app.getContainerImage().setBaseImage(this.application.getContainerImage().getBaseImage());
}
else if (StringUtils.hasText(this.global.getApplication().getContainerImage().getBaseImage())) {
app.getContainerImage().setBaseImage(this.global.getApplication().getContainerImage().getBaseImage());
}
// Generator Properties
ProjectGeneratorProperties generatorProperties = new ProjectGeneratorProperties();
// ----------------------------------------------------------------------------------------------------------
// Binders Configuration
// ----------------------------------------------------------------------------------------------------------
Map<String, Binder> allBinders = new HashMap<>(this.global.getBinders());
// Note: The Application concrete Binder definitions will replace any global
// Binder definitions with the same names. E.g. you can replace but not extend global binders configurations.
allBinders.putAll(this.binders);
if (CollectionUtils.isEmpty(allBinders)) {
throw new MojoExecutionException("At least one Binder configuration is required!");
}
// Converts Mojo's Binder configurations into BinderDefinitions.
List<BinderDefinition> bindersDefinitions = allBinders.entrySet().stream()
.map(es -> {
BinderDefinition bd = new BinderDefinition();
bd.setName(es.getKey());
bd.setProperties(es.getValue().getProperties());
bd.getMaven().setProperties(es.getValue().getMaven().getProperties()
.entrySet().stream()
.map(pes -> "<" + pes.getKey() + ">" + pes.getValue() + "</" + pes.getKey() + ">")
.collect(Collectors.toList()));
bd.getMaven().setDependencies(
es.getValue().getMaven().getDependencies().stream()
.map(MavenXmlWriter::toXml)
.map(xml -> MavenXmlWriter.indent(xml, 8))
.collect(Collectors.toList()));
bd.getMaven().setDependencyManagement(
es.getValue().getMaven().getDependencyManagement().stream()
.filter(Objects::nonNull)
.peek(dependency -> {
dependency.setScope("import");
dependency.setType("pom");
})
.map(MavenXmlWriter::toXml)
.map(xml -> MavenXmlWriter.indent(xml, 12))
.collect(Collectors.toList()));
bd.getMaven().setPlugins(
es.getValue().getMaven().getPlugins().stream()
.map(MavenXmlWriter::toXml)
.map(d -> MavenXmlWriter.indent(d, 12))
.collect(Collectors.toList()));
bd.getMaven().setRepositories(es.getValue().getMaven().getRepositories().stream()
.map(MavenXmlWriter::toXml)
.map(d -> MavenXmlWriter.indent(d, 12))
.collect(Collectors.toList()));
return bd;
})
.collect(Collectors.toList());
app.setBootPluginConfiguration(this.application.getBootPluginConfiguration());
// ----------------------------------------------------------------------------------------------------------
// Project Generator
// ----------------------------------------------------------------------------------------------------------
generatorProperties.setAppDefinition(app);
generatorProperties.setBinderDefinitions(bindersDefinitions);
generatorProperties.setOutputFolder(new File(this.generatedProjectHome));
generatorProperties.setProjectResourcesDirectory(this.projectResourcesDir);
try {
ProjectGenerator.getInstance().generate(generatorProperties);
}
catch (IOException e) {
throw new MojoFailureException("Project generation failure", e);
}
}
private boolean isSameArtifact(Dependency dep1, Dependency dep2) {
return dep1.getGroupId().equalsIgnoreCase(dep2.getGroupId())
&& dep1.getArtifactId().equalsIgnoreCase(dep2.getArtifactId());
}
/**
* If the visible metadata properties files are provided in the source project, add theirs' type and name filters
* to the existing visible configurations.
*
* @param sourceTypeFilters existing source type filters configured via the mojo parameter.
* @param nameFilters existing name filters configured via the mojo parameter.
*/
private void populateVisiblePropertiesFromFile(List<String> sourceTypeFilters, List<String> nameFilters) {
if (this.projectResourcesDir == null || !projectResourcesDir.exists()) {
return;
}
Optional<Properties> optionalProperties =
loadVisiblePropertiesFromResource(FileUtils.getFile(projectResourcesDir, "META-INF",
VISIBLE_PROPERTIES_FILE_NAME));
optionalProperties.ifPresent(properties -> {
addToFilters(properties.getProperty(CONFIGURATION_PROPERTIES_CLASSES), sourceTypeFilters);
addToFilters(properties.getProperty(CONFIGURATION_PROPERTIES_NAMES), nameFilters);
});
if (optionalProperties.isPresent()) {
return;
}
loadVisiblePropertiesFromResource(FileUtils.getFile(
projectResourcesDir, "META-INF", DEPRECATED_WHITELIST_FILE_NAME))
.ifPresent(properties -> {
getLog().warn(DEPRECATED_WHITELIST_FILE_NAME + " is deprecated." +
" Please use " + VISIBLE_PROPERTIES_FILE_NAME + " instead");
addToFilters(properties.getProperty(CONFIGURATION_PROPERTIES_CLASSES), sourceTypeFilters);
addToFilters(properties.getProperty(CONFIGURATION_PROPERTIES_NAMES), nameFilters);
});
}
private Optional<Properties> loadVisiblePropertiesFromResource(File visiblePropertiesFile) {
if (visiblePropertiesFile.exists()) {
Properties properties = new Properties();
try (InputStream is = new FileInputStream(visiblePropertiesFile)) {
properties.load(is);
}
catch (Exception e) {
return Optional.empty();
}
return Optional.of(properties);
}
return Optional.empty();
}
private void addToFilters(String csvFilterProperties, List<String> filterList) {
if (!StringUtils.isEmpty(csvFilterProperties)) {
for (String filterProperty : csvFilterProperties.trim().split(",")) {
if (StringUtils.hasText(filterProperty)) {
if (!filterList.contains(filterProperty.trim())) {
filterList.add(filterProperty.trim());
}
}
}
}
}
/**
* Section that allow to define Application and Binder configurations common across multiple applications
* and binders.
*/
public static class Global {
private final Application application = new Application();
private final Map<String, Binder> binders = new HashMap<>();
public Application getApplication() {
return application;
}
public Map<String, Binder> getBinders() {
return binders;
}
}
/**
* Allow defining the target application configuration, excluding the Binder specific configurations.
*/
public static class Application {
/**
* Unique name of the application for the given type.
* The generated binder specific, applications have names that combine this application name, type as well
* as the name of the binder they are produced for.
*/
private String name;
/**
* Application type. Cloud be of type (source, processor, sink).
*/
private AppDefinition.AppType type;
/**
* Version to be used for the generated application.
*/
private String version;
/**
* The Spring Boot version the generated application is configured to inherit from.
*/
private String bootVersion;
/**
* Spring configuration class used to instantiate the application.
*/
private String configClass;
/**
* Group Id to use with the application.
*/
private String groupId = "org.springframework.cloud.stream.app";
/**
* The Spring Cloud Function definition. Could be a composition definition as well.
*/
private String functionDefinition;
/**
* Custom application properties to contribute to the generated application.properties file.
*/
private Map<String, String> properties = new HashMap<>();
/**
* Parameters used to tune the Application metadata generation.
*/
private final Metadata metadata = new Metadata();
private final ContainerImage containerImage = new ContainerImage();
private final Maven maven = new Maven();
private String bootPluginConfiguration;
public String getBootPluginConfiguration() {
return bootPluginConfiguration;
}
public void setBootPluginConfiguration(String bootPluginConfiguration) {
this.bootPluginConfiguration = bootPluginConfiguration;
}
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Maven getMaven() {
return maven;
}
public Metadata getMetadata() {
return metadata;
}
public ContainerImage getContainerImage() {
return containerImage;
}
public String getBootVersion() {
return bootVersion;
}
public void setBootVersion(String bootVersion) {
this.bootVersion = bootVersion;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public AppDefinition.AppType getType() {
return type;
}
public void setType(AppDefinition.AppType type) {
this.type = type;
}
public String getConfigClass() {
return configClass;
}
public void setConfigClass(String configClass) {
this.configClass = configClass;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String appGroupId) {
this.groupId = appGroupId;
}
public String getFunctionDefinition() {
return (!StringUtils.isEmpty(this.functionDefinition)) ? this.functionDefinition :
this.name + this.functionType();
}
public void setFunctionDefinition(String functionDefinition) {
this.functionDefinition = functionDefinition;
}
private String functionType() {
switch (this.type) {
case processor:
return "Function";
case sink:
return "Consumer";
case source:
return "Supplier";
}
throw new IllegalArgumentException("Unknown App type:" + this.type);
}
/**
* Parameters used to tune the Application metadata generation. It allows to set a type or name based filters
* that filer-in the subset all application configuration metadata. In addition to the explicitly specified
* filters, the Mojo automatically populates the filters with values found in the
* 'dataflow-configuration-metadata-whitelist.properties" or 'dataflow-configuration-metadata.properties' files.
*
* You can also specify the version of the metadata generation plugin.
*/
public static class Metadata {
/**
* list of source types from application's configuration metadata to include in the visible metadata.
*/
private List<String> sourceTypeFilters = new ArrayList<>();
/**
* list of property names from application's configuration metadata to include in the visible metadata.
*/
private List<String> nameFilters = new ArrayList<>();
/**
* Version of the metadata maven plugin to be used.
*/
private String mavenPluginVersion;
public List<String> getSourceTypeFilters() {
return sourceTypeFilters;
}
public void setSourceTypeFilters(List<String> sourceTypeFilters) {
this.sourceTypeFilters = sourceTypeFilters;
}
public List<String> getNameFilters() {
return nameFilters;
}
public void setNameFilters(List<String> nameFilters) {
this.nameFilters = nameFilters;
}
public String getMavenPluginVersion() {
return mavenPluginVersion;
}
public void setMavenPluginVersion(String mavenPluginVersion) {
this.mavenPluginVersion = mavenPluginVersion;
}
}
/**
* Configuration for injection the application's visible, configuration metadata into the Container image.
*/
public static class ContainerImage {
/**
* Target container image. Docker and OCI are supported. If not specified the Docker is selected.
*/
private AppDefinition.ContainerImageFormat format = null;
/**
* Target container image organization name.
*/
private String orgName;
/**
* Base images to be used by the target container image.
*/
private String baseImage;
/**
* Enable or disable the inclusion of application's metadata into the image's labels.
*/
private boolean enableMetadata = true;
public AppDefinition.ContainerImageFormat getFormat() {
return format;
}
public void setFormat(AppDefinition.ContainerImageFormat format) {
this.format = format;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public boolean isEnableMetadata() {
return enableMetadata;
}
public void setEnableMetadata(boolean enableMetadata) {
this.enableMetadata = enableMetadata;
}
public String getBaseImage() {
return baseImage;
}
public void setBaseImage(String baseImage) {
this.baseImage = baseImage;
}
}
}
/**
* Binder specific properties to be applied in addition to the {@link Application} parameters.
*/
public static class Binder {
/**
* Application properties specific for the binder. Contributed to the generated application.properties file.
*/
private List<String> properties = new ArrayList<>();
/**
* Binder specific maven configurations. Applied in addition to the {@link Application}'s maven configurations.
*/
private final Maven maven = new Maven();
public Maven getMaven() {
return maven;
}
public List<String> getProperties() {
return properties;
}
public void setProperties(List<String> properties) {
this.properties = properties;
}
}
/**
* Maven configuration used inside the {@link Application} and {@link Binder} configurations.
*/
public static class Maven {
/**
* Custom maven properties. Contributed to the generated POM's properties section.
*/
private Map<String, String> properties = new HashMap<>();
/**
* Custom maven management dependencies. Contributed to the generated POM's dependencyManagement section.
*/
private List<Dependency> dependencyManagement = new ArrayList<>();
/**
* Custom maven dependencies. Contributed to the generated POM's dependencies section.
*/
private List<Dependency> dependencies = new ArrayList<>();
/**
* Custom maven plugins. Contributed to the generated POM's plugins section.
*
* Note: if the plugin definition uses a configuration block then the content must be wrapped within a
* <pre>@code{
* <![CDATA[ ... ]]>
* }</pre> section! Find more: {@link MavenXmlWriter#toXml(Plugin)}.
*/
private List<Plugin> plugins = new ArrayList<>();
/**
* Custom maven repositories. Contributed to the generated POM's repositories section.
*/
private List<Repository> repositories = new ArrayList<>();
public Map<String, String> getProperties() {
return properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public List<Dependency> getDependencyManagement() {
return dependencyManagement;
}
public void setDependencyManagement(List<Dependency> dependencyManagement) {
this.dependencyManagement = dependencyManagement;
}
public List<Dependency> getDependencies() {
return dependencies;
}
public void setDependencies(List<Dependency> dependencies) {
this.dependencies = dependencies;
}
public List<Plugin> getPlugins() {
return plugins;
}
public void setPlugins(List<Plugin> plugins) {
this.plugins = plugins;
}
public List<Repository> getRepositories() {
return repositories;
}
public void setRepositories(List<Repository> repositories) {
this.repositories = repositories;
}
}
}

View File

@@ -0,0 +1,308 @@
/*
* Copyright 2020-2022 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.dataflow.app.plugin.generator;
import java.util.ArrayList;
import java.util.List;
/**
* Application configurations used to parameterize the project templates.
*
* @author Christian Tzolov
* @author Soby Chacko
*/
public class AppDefinition {
/**
* Marker 'configClass' value that indicates that this is an autoconfiguration and the @Import statement should be
* dropped.
*/
public static final String AUTOCONFIGURATION_MARKER = "AUTOCONFIGURATION";
/**
* Application name.
*/
private String name;
/**
* Application type.
*/
private AppType type;
/**
* Application version.
*/
private String version;
/**
* Spring Boot version used as a parent by the application.
*/
private String springBootVersion;
/**
* Application's backing Spring configuration class.
*/
private String configClass;
/**
* Application's group ID.
*/
private String groupId;
/**
* Spring Cloud Function definition.
*/
private String functionDefinition;
/**
* Additional application properties.
*/
private List<String> properties = new ArrayList<>();
/**
* Maven related configurations to be applied for this App definition.
*/
private final MavenDefinition maven = new MavenDefinition();
/**
* ContainerImage related configurations to be applied for this App definition.
*/
private final ContainerImage containerImage = new ContainerImage();
/**
* Application metadata related configurations to be applied for this App definition.
*/
private final Metadata metadata = new Metadata();
private String bootPluginConfiguration;
public String getBootPluginConfiguration() {
return bootPluginConfiguration;
}
public void setBootPluginConfiguration(String bootPluginConfiguration) {
this.bootPluginConfiguration = bootPluginConfiguration;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AppType getType() {
return type;
}
public void setType(AppType type) {
this.type = type;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getSpringBootVersion() {
return springBootVersion;
}
public void setSpringBootVersion(String springBootVersion) {
this.springBootVersion = springBootVersion;
}
public List<String> getProperties() {
return properties;
}
public void setProperties(List<String> properties) {
this.properties = properties;
}
public MavenDefinition getMaven() {
return maven;
}
public ContainerImage getContainerImage() {
return containerImage;
}
public Metadata getMetadata() {
return metadata;
}
public boolean isSupplier() {
return type == AppType.source;
}
public boolean isConsumer() {
return type == AppType.sink;
}
public boolean isFunction() {
return type == AppType.processor;
}
public boolean isAutoconfiguration() {
return AUTOCONFIGURATION_MARKER.equalsIgnoreCase(configClass.trim());
}
public String getConfigClass() {
return configClass;
}
public void setConfigClass(String configClass) {
this.configClass = configClass;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getFunctionDefinition() {
return functionDefinition;
}
public void setFunctionDefinition(String functionDefinition) {
this.functionDefinition = functionDefinition;
}
public enum ContainerImageFormat {
/**
* Docker image format.
*/
Docker,
/**
* OCI format.
*/
OCI
}
public enum AppType {
/**
* source type.
*/
source,
/**
* processor type.
*/
processor,
/**
* sink type.
*/
sink
}
public static class ContainerImage {
/**
* Allow to generate either Docker or OCI image formats.
*/
private ContainerImageFormat format = ContainerImageFormat.Docker;
/**
* True will attempt to inline the (white) filtered Spring Boot metadata as Base64 encoded property.
*/
private boolean enableMetadata = false;
private String orgName = "springcloudstream";
private String tag = "latest";
private String baseImage = "springcloud/baseimage:1.0.4";
public ContainerImageFormat getFormat() {
return format;
}
public void setFormat(ContainerImageFormat format) {
this.format = format;
}
public boolean isEnableMetadata() {
return enableMetadata;
}
public void setEnableMetadata(boolean enableMetadata) {
this.enableMetadata = enableMetadata;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getBaseImage() {
return baseImage;
}
public void setBaseImage(String baseImage) {
this.baseImage = baseImage;
}
}
public static class Metadata {
private List<String> sourceTypeFilters = new ArrayList<>();
private List<String> nameFilters = new ArrayList<>();
private String mavenPluginVersion;
public List<String> getSourceTypeFilters() {
return sourceTypeFilters;
}
public void setSourceTypeFilters(List<String> sourceTypeFilters) {
this.sourceTypeFilters = sourceTypeFilters;
}
public List<String> getNameFilters() {
return nameFilters;
}
public void setNameFilters(List<String> nameFilters) {
this.nameFilters = nameFilters;
}
public String getMavenPluginVersion() {
return mavenPluginVersion;
}
public void setMavenPluginVersion(String mavenPluginVersion) {
this.mavenPluginVersion = mavenPluginVersion;
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2020-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.dataflow.app.plugin.generator;
import java.util.ArrayList;
import java.util.List;
/**
* Binder configurations used to parameterize the project templates.
*
* @author Christian Tzolov
*/
public class BinderDefinition {
/**
* Binder name.
*/
private String name;
/**
* Binder specific, Maven configurations contributed to the generated poms.
*/
private final MavenDefinition maven = new MavenDefinition();
/**
* Binder specific application properties contributed to the generated application.properties.
*/
private List<String> properties = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MavenDefinition getMaven() {
return maven;
}
public List<String> getProperties() {
return properties;
}
public void setProperties(List<String> properties) {
this.properties = properties;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2020-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.dataflow.app.plugin.generator;
import java.util.ArrayList;
import java.util.List;
/**
* Maven configurations used by the {@link AppDefinition} and {@link BinderDefinition} to parametrize the generated POMs.
*
* @author Christian Tzolov
*/
public class MavenDefinition {
/**
* Contributes the properties to the Maven's properties section.
*/
private List<String> properties = new ArrayList<>();
/**
* Contributes the dependencyManagement entries to the Maven's dependencyManagement dependencies.
*/
private List<String> dependencyManagement = new ArrayList<>();
/**
* Contributes the dependency entries to the Maven's dependencies section.
*/
private List<String> dependencies = new ArrayList<>();
/**
* Contributes the plugin entries to the Maven's plugins section.
*/
private List<String> plugins = new ArrayList<>();
/**
* Contributes the repository entries to the Maven's repositories section.
*/
private List<String> repositories = new ArrayList<>();
public List<String> getProperties() {
return properties;
}
public void setProperties(List<String> properties) {
this.properties = properties;
}
public List<String> getDependencyManagement() {
return dependencyManagement;
}
public void setDependencyManagement(List<String> dependencyManagement) {
this.dependencyManagement = dependencyManagement;
}
public List<String> getDependencies() {
return dependencies;
}
public void setDependencies(List<String> dependencies) {
this.dependencies = dependencies;
}
public List<String> getPlugins() {
return plugins;
}
public void setPlugins(List<String> plugins) {
this.plugins = plugins;
}
public List<String> getRepositories() {
return repositories;
}
public void setRepositories(List<String> repositories) {
this.repositories = repositories;
}
}

View File

@@ -0,0 +1,237 @@
/*
* Copyright 2020-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.dataflow.app.plugin.generator;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import org.apache.commons.io.FileUtils;
import org.springframework.util.Assert;
/**
* @author Christian Tzolov
*/
public final class ProjectGenerator {
private ProjectGenerator() {
}
public static ProjectGenerator getInstance() {
return new ProjectGenerator();
}
// Utils
public static File mkdirs(File dir) {
if (!dir.exists()) {
dir.mkdirs();
}
if (!dir.isDirectory()) {
throw new IllegalStateException("Not a directory: " + dir);
}
return dir;
}
public static void copy(String content, File file) throws IOException {
Files.copy(new ByteArrayInputStream(content.getBytes()), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
public static String toPkg(String text) {
return text.replace("-", ".");
}
public static String capitalize(String text) {
return text.substring(0, 1).toUpperCase() + text.substring(1);
}
public static String camelCase(String text) {
return Arrays.stream(text.split("-")).reduce("", (r, p) -> r + capitalize(p));
}
public static File pkgToDir(File parent, String packageName) {
String[] names = packageName.split("\\.");
File result = parent;
for (String p : names) {
result = file(result, p);
}
return result;
}
public static File file(File parent, String child) {
return new File(parent, child);
}
public void generate(ProjectGeneratorProperties generatorProperties) throws IOException {
Map<String, Object> modulesTemplateProperties = new HashMap<>();
// register {{#capitalize}}...{{/capitalize}} function.
modulesTemplateProperties.put("capitalize", (Mustache.Lambda) (frag, out) -> out.write(capitalize(frag.execute().trim())));
// register {{#camelCase}}...{{/camelCase}} function.
modulesTemplateProperties.put("camelCase", (Mustache.Lambda) (frag, out) -> out.write(camelCase(frag.execute().trim())));
modulesTemplateProperties.put("app", generatorProperties.getAppDefinition());
modulesTemplateProperties.put("binders", generatorProperties.getBinders());
// ---------------------------------
// Generate apps container POM
// ---------------------------------
File appParentDir = mkdirs(generatorProperties.getOutputFolder());
copy(materialize("template/apps-modules-pom.xml", modulesTemplateProperties),
file(appParentDir, "pom.xml"));
// maven wrapper
copyMavenWrapper(appParentDir);
// ---------------------------------
// Generate App projects
// ---------------------------------
Assert.notEmpty(generatorProperties.getBinders(), "At least one Binder must be provided");
for (BinderDefinition binder : generatorProperties.getBinders()) {
generateAppProject(appParentDir, modulesTemplateProperties, generatorProperties.getAppDefinition(),
generatorProperties.getProjectResourcesDirectory(), binder);
}
}
private void generateAppProject(File appRootDirectory, Map<String, Object> containerTemplateProperties,
AppDefinition appDefinition, File projectResourcesDirectory, BinderDefinition binder) throws IOException {
String appClassName = String.format("%s%s%sApplication",
camelCase(appDefinition.getName()),
capitalize(appDefinition.getType().name()),
capitalize(binder.getName()));
String appPackageName = String.format("%s.%s.%s.%s",
appDefinition.getGroupId(),
toPkg(appDefinition.getName()),
appDefinition.getType(),
binder.getName());
Map<String, Object> appTemplateProperties = new HashMap<>(containerTemplateProperties);
// Shortcut substitutions. Prevent complicated expressions in the templates.
appTemplateProperties.put("app-class-name", appClassName);
appTemplateProperties.put("app-package-name", appPackageName);
appTemplateProperties.put("app-binder", binder);
// app POM
File appDir =
mkdirs(file(appRootDirectory, appDefinition.getName() + "-" + appDefinition.getType() + "-" + binder.getName()));
copy(materialize("template/app-pom.xml", appTemplateProperties), file(appDir, "pom.xml"));
File appMainSrcDir = mkdirs(pkgToDir(appDir, "src.main.java." + appPackageName));
File appMainResourceDir = mkdirs(pkgToDir(appDir, "src.main.resources"));
// copy the entire project's src/main/resources directory
if (projectResourcesDirectory != null && projectResourcesDirectory.exists()) {
FileUtils.copyDirectory(projectResourcesDirectory, appMainResourceDir);
}
// application.properties
String materializedAppProperties = materialize("template/app.properties", appTemplateProperties);
// Note: the application properties file may already exist from the parent's project src/main/resources dir.
File appPropertyFile = file(appMainResourceDir, "application.properties");
//FileUtils.writeStringToFile(appPropertyFile, materializedAppProperties, "UTF8", true);
Properties appProps = new Properties();
if (appPropertyFile.exists()) {
appProps.load(new FileInputStream(appPropertyFile));
}
Properties genAppProps = new Properties();
genAppProps.load(new ByteArrayInputStream(materializedAppProperties.getBytes(StandardCharsets.UTF_8)));
// Generated properties override the inherited!
appProps.putAll(genAppProps);
appProps.store(new FileWriter(appPropertyFile), "App generator properties");
copy(materialize("template/App.java", appTemplateProperties),
file(appMainSrcDir, appClassName + ".java"));
// TESTS
File appTestSrcDir = mkdirs(pkgToDir(appDir, "src.test.java." + appPackageName));
copy(materialize("template/AppTests.java", appTemplateProperties),
file(appTestSrcDir, appClassName + "Tests.java"));
// README
copy(materialize("template/README.adoc", appTemplateProperties),
file(appDir, "README.adoc"));
// maven wrapper
copyMavenWrapper(appDir);
}
private void copyMavenWrapper(File appDir) throws IOException {
// mvnw
copyResource("template/mvnw", file(appDir, "mvnw"));
file(appDir, "mvnw").setExecutable(true);
copyResource("template/mvnw.cmd", file(appDir, "mvnw.cmd"));
File dotMavenDir = mkdirs(new File(appDir, ".mvn"));
// .mvn/jvm.config
copyResource("template/.mvn/jvm.config", file(dotMavenDir, "jvm.config"));
// .mvn/maven.config
copyResource("template/.mvn/maven.config", file(dotMavenDir, "maven.config"));
File dotMavenWrapper = mkdirs(file(appDir, ".mvn/wrapper"));
// .mvn/wrapper/maven-wrapper.jar
copyResource("template/.mvn/wrapper/maven-wrapper.jar",
file(dotMavenWrapper, "maven-wrapper.jar"));
// .mvn/wrapper/maven-wrapper.properties
copyResource("template/.mvn/wrapper/maven-wrapper.properties",
file(dotMavenWrapper, "maven-wrapper.properties"));
// .mvn/wrapper/MavenWrapperDownloader.java
copyResource("template/.mvn/wrapper/MavenWrapperDownloader.java",
file(dotMavenWrapper, "MavenWrapperDownloader.java"));
}
private String materialize(String templatePath, Map<String, Object> templateProperties) throws IOException {
try (InputStreamReader resourcesTemplateReader = new InputStreamReader(
Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(templatePath)))) {
Template resourceTemplate = Mustache.compiler().escapeHTML(false).compile(resourcesTemplateReader);
return resourceTemplate.execute(templateProperties);
}
}
private void copyResource(String resourcePath, File toFile) throws IOException {
try (InputStream resourcesStream = Objects.requireNonNull(
this.getClass().getClassLoader().getResourceAsStream(resourcePath))) {
Files.copy(resourcesStream, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2020-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.dataflow.app.plugin.generator;
import java.io.File;
import java.util.List;
/**
* @author Christian Tzolov
*/
public class ProjectGeneratorProperties {
/**
* Source, processor and sink application configuration.
*/
private AppDefinition appDefinition;
/**
* Location where the project source files are written.
*/
private File outputFolder = new File("./target/output");
/**
* List of binders to generate applications for.
*/
private List<BinderDefinition> binders;
/**
* The location of a project's src/main/resources directory.
*/
private File projectResourcesDirectory;
public File getOutputFolder() {
return outputFolder;
}
public void setOutputFolder(File outputFolder) {
this.outputFolder = outputFolder;
}
public AppDefinition getAppDefinition() {
return appDefinition;
}
public void setAppDefinition(AppDefinition appDefinition) {
this.appDefinition = appDefinition;
}
public List<BinderDefinition> getBinders() {
return binders;
}
public void setBinderDefinitions(List<BinderDefinition> binders) {
this.binders = binders;
}
public File getProjectResourcesDirectory() {
return projectResourcesDirectory;
}
public void setProjectResourcesDirectory(File projectResourcesDirectory) {
this.projectResourcesDirectory = projectResourcesDirectory;
}
}

View File

@@ -0,0 +1 @@
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom

View File

@@ -0,0 +1 @@
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring

View File

@@ -0,0 +1,110 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL =
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: : " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

View File

@@ -0,0 +1 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip

View File

@@ -0,0 +1,33 @@
/*
* 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 {{app-package-name}};
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
{{^app.autoconfiguration}}
@Import({ {{app.configClass}} })
{{/app.autoconfiguration}}
public class {{app-class-name}} {
public static void main(String[] args) {
SpringApplication.run({{app-class-name}}.class, args);
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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
*
* 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.
*/
package {{app-package-name}};
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class {{app-class-name}}Tests {
@Test
public void contextLoads() {
}
}

View File

@@ -0,0 +1 @@
= Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder.name}}{{/capitalize}} Binder Application

View File

@@ -0,0 +1,309 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>{{app.springBootVersion}}</version>
<relativePath></relativePath>
</parent>
<groupId>{{app.groupId}}</groupId>
<artifactId>{{app.name}}-{{app.type}}-{{app-binder.name}}</artifactId>
<version>{{app.version}}</version>
<name>{{app.name}}-{{app.type}}-{{app-binder.name}}</name>
<description>Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder.name}}{{/capitalize}} Binder Application
</description>
<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>
<properties>
<skipTests>true</skipTests>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- (begin) maven properties -->
{{#app.maven.properties}}
{{this}}
{{/app.maven.properties}}
{{#app-binder.maven.properties}}
{{this}}
{{/app-binder.maven.properties}}
<!-- (end) maven properties -->
</properties>
<dependencyManagement>
<dependencies>
<!-- (begin) managed dependencies -->
{{#app-binder.maven.dependencyManagement}}
{{this}}
{{/app-binder.maven.dependencyManagement}}
{{#app.maven.dependencyManagement}}
{{this}}
{{/app.maven.dependencyManagement}}
<!-- (end) managed dependencies -->
</dependencies>
</dependencyManagement>
<dependencies>
<!-- (begin) additional dependencies -->
{{#app.maven.dependencies}}
{{this}}
{{/app.maven.dependencies}}
{{#app-binder.maven.dependencies}}
{{this}}
{{/app-binder.maven.dependencies}}
<!-- (end) additional dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<!-- (begin) additional repositories -->
{{#app.maven.repositories}}
{{this}}
{{/app.maven.repositories}}
{{#app-binder.maven.repositories}}
{{this}}
{{/app-binder.maven.repositories}}
<!-- (end) additional repositories -->
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<!-- (begin) additional plugins -->
{{#app.maven.plugins}}
{{this}}
{{/app.maven.plugins}}
{{#app-binder.maven.plugins}}
{{this}}
{{/app-binder.maven.plugins}}
<!-- (end) additional plugins -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
{{#app.bootPluginConfiguration}}
<configuration>
{{this}}
</configuration>
{{/app.bootPluginConfiguration}}
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<from>
<image>{{app.containerImage.baseImage}}</image>
</from>
<to>
<image>{{app.containerImage.orgName}}/${project.artifactId}:{{app.containerImage.tag}}</image>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<format>{{app.containerImage.format}}</format>
{{#app.containerImage.enableMetadata}}
<labels>
<org.springframework.cloud.dataflow.spring-configuration-metadata.json>
${org.springframework.cloud.dataflow.spring.configuration.metadata.json}
</org.springframework.cloud.dataflow.spring-configuration-metadata.json>
<application.name>${project.artifactId}</application.name>
<application.version>${project.version}</application.version>
</labels>
{{/app.containerImage.enableMetadata}}
</container>
</configuration>
</plugin>
{{#app.containerImage.enableMetadata}}
<!--
Use of properties-maven-plugin to load the encoded metadata properties created by the jib-maven-plugin
Use: Use: ./mvnw clean install jib:dockerBuild or ./mvnw clean install jib:build
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>
${project.build.outputDirectory}/META-INF/spring-configuration-metadata-encoded.properties
</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
{{/app.containerImage.enableMetadata}}
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-metadata-plugin</artifactId>
<version>{{app.metadata.mavenPluginVersion}}</version>
<configuration>
{{#app.containerImage.enableMetadata}}
<storeFilteredMetadata>true</storeFilteredMetadata>
<metadataFilter>
<names>
{{#app.metadataNameFilters}}
<filter>{{this}}</filter>
{{/app.metadataNameFilters}}
</names>
<sourceTypes>
{{#app.metadata.sourceTypeFilters}}
<filter>{{this}}</filter>
{{/app.metadata.sourceTypeFilters}}
</sourceTypes>
</metadataFilter>
{{/app.containerImage.enableMetadata}}
</configuration>
<executions>
<execution>
<id>aggregate-metadata</id>
<phase>compile</phase>
<goals>
<goal>aggregate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>javadoc</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<quiet>true</quiet>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<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>
<version>1.6</version>
<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>
</project>

View File

@@ -0,0 +1,30 @@
spring.application.name=${vcap.application.name:{{app.name}}-{{app.type}}}
info.app.name=@project.artifactId@
info.app.description=@project.description@
info.app.version=@project.version@
management.endpoints.web.exposure.include=health,info,bindings
management.endpoints.web.discovery.enabled=true
management.info.env.enabled=true
spring.cloud.function.definition={{app.functionDefinition}}
{{#app.consumer}}
spring.cloud.stream.function.bindings.{{app.functionDefinition}}-in-0=input
{{/app.consumer}}
{{#app.supplier}}
spring.cloud.stream.function.bindings.{{app.functionDefinition}}-out-0=output
{{/app.supplier}}
{{#app.function}}
spring.cloud.stream.function.bindings.{{app.functionDefinition}}-in-0=input
spring.cloud.stream.function.bindings.{{app.functionDefinition}}-out-0=output
{{/app.function}}
{{#app.properties}}
{{this}}
{{/app.properties}}
{{#app-binder.properties}}
{{this}}
{{/app-binder.properties}}

View File

@@ -0,0 +1,103 @@
<?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>{{app.groupId}}</groupId>
<artifactId>{{app.name}}-{{app.type}}-apps</artifactId>
<version>{{app.version}}</version>
<packaging>pom</packaging>
<name>Apps Modules</name>
<description>Parent project for generated apps project modules</description>
<url>http://spring.io/spring-cloud</url>
<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>
<developers>
<developer>
<id>schacko</id>
<name>Soby Chacko</name>
<email>schacko at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>http://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>tzolov</id>
<name>Christian Tzolov</name>
<email>ctzolov at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>http://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<modules>
{{#binders}}
<module>{{app.name}}-{{app.type}}-{{this.name}}</module>
{{/binders}}
</modules>
<scm>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-stream-app-starters.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-stream-app-starters.git</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-stream-app-starters</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>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>
<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>
</profiles>
</project>

View File

@@ -0,0 +1,254 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
echo "Running version check"
VERSION=$( sed '\!<parent!,\!</parent!d' `dirname $0`/pom.xml | grep '<version' | head -1 | sed -e 's/.*<version>//' -e 's!</version>.*$!!' )
echo "The found version is [${VERSION}]"
if echo $VERSION | egrep -q 'M|RC'; then
echo Activating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
else
if echo $VERSION | egrep -q 'SNAPSHOT'; then
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
echo Deactivating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//')
else
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
echo Activating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral"
fi
fi
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"

View File

@@ -0,0 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@@ -0,0 +1,15 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 https://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${artifactId}</id>
<dependencySets>
<dependencySet>
<includes>
<include>${groupId}:${artifactId}</include>
</includes>
<outputDirectory>.</outputDirectory>
<outputFileNameMapping>${artifactId}.jar</outputFileNameMapping>
</dependencySet>
</dependencySets>
</assembly>

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2020-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.dataflow.app.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.plugin.testing.MojoRule;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Christian Tzolov
*/
public class MojoHarnessTest {
@Rule
public TemporaryFolder projectHome = new TemporaryFolder();
@Rule
public MojoRule mojoRule = new MojoRule();
@Test
public void testSomething() throws Exception {
File pomRoot = new File("src/test/resources/unit/http-source-apps/");
SpringCloudStreamAppGeneratorMojo myMojo = (SpringCloudStreamAppGeneratorMojo)
mojoRule.lookupConfiguredMojo(pomRoot, "generate-app");
assertThat(myMojo).isNotNull();
myMojo.execute();
assertThat(new File("./target/apps/http-source-kafka/src/main/resources/test.txt")).exists();
assertThat(new File("./target/apps/http-source-kafka/src/main/resources/application.properties")).exists();
Properties applicationProperties = new Properties();
applicationProperties.load(new FileReader("./target/apps/http-source-kafka/src/main/resources/application.properties"));
assertThat(applicationProperties.getProperty("spring.cloud.function.definition")).isEqualTo("httpSupplier");
assertThat(applicationProperties.getProperty("static.property")).isEqualTo("bla");
Model pomModel = getModel(new File("./target/apps"));
List<Dependency> dependencies = pomModel.getDependencies();
assertThat(dependencies.size()).isEqualTo(15);
assertThat(dependencies.stream()
.filter(d -> d.getArtifactId().equals("http-supplier")).count()).isEqualTo(1);
assertThat(dependencies.stream()
.filter(d -> d.getArtifactId().equals("stream-applications-postprocessor-common")).count()).isEqualTo(1);
assertThat(dependencies.stream()
.filter(d -> d.getArtifactId().equals("spring-cloud-stream-binder-kafka")).count()).isEqualTo(1);
Parent parent = pomModel.getParent();
assertThat(parent.getArtifactId()).isEqualTo("spring-boot-starter-parent");
assertThat(parent.getVersion()).isEqualTo("2.3.0.M1");
assertThat(pomModel.getArtifactId()).isEqualTo("http-source-kafka");
assertThat(pomModel.getGroupId()).isEqualTo("org.springframework.cloud.stream.app.test");
assertThat(pomModel.getName()).isEqualTo("http-source-kafka");
assertThat(pomModel.getVersion()).isEqualTo("3.0.0.BUILD-SNAPSHOT");
assertThat(pomModel.getDescription()).isEqualTo("Spring Cloud Stream Http Source Kafka Binder Application");
List<Plugin> plugins = pomModel.getBuild().getPlugins();
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("spring-boot-maven-plugin")).count()).isEqualTo(1);
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("properties-maven-plugin")).count()).isEqualTo(1);
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).count()).isEqualTo(1);
Plugin jibPlugin = plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).findFirst().get();
assertThat(jibPlugin.getConfiguration().toString())
.contains("<org.springframework.cloud.dataflow.spring-configuration-metadata.json>" +
"${org.springframework.cloud.dataflow.spring.configuration.metadata.json}" +
"</org.springframework.cloud.dataflow.spring-configuration-metadata.json>");
assertThat(jibPlugin.getConfiguration().toString()).contains("<image>testspringcloud/${project.artifactId}:3.0.0.BUILD-SNAPSHOT</image>");
assertThat(jibPlugin.getConfiguration().toString()).contains("<image>globalBaseImage</image>");
assertThat(pomModel.getRepositories().size()).isEqualTo(5);
assertThat(pomModel.getRepositories().stream().map(r -> r.getId()).collect(Collectors.toList()))
.contains("bintray-global", "bintray-application", "bintray-binder");
}
private Model getModel(File rootPath) {
File pomXml = new File(new File(rootPath, "http-source-kafka"), "pom.xml");
try (InputStream is = new FileInputStream(pomXml)) {
return new MavenXpp3Reader().read(is);
}
catch (IOException | XmlPullParserException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2020-2022 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.dataflow.app.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.cloud.dataflow.app.plugin.generator.AppDefinition;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Christian Tzolov
* @author Soby Chacko
*/
public class SpringCloudStreamAppGeneratorMojoTest {
@Rule
public TemporaryFolder projectHome = new TemporaryFolder();
private SpringCloudStreamAppGeneratorMojo springCloudStreamAppMojo = new SpringCloudStreamAppGeneratorMojo();
private Class<? extends SpringCloudStreamAppGeneratorMojo> mojoClazz = springCloudStreamAppMojo.getClass();
private SpringCloudStreamAppGeneratorMojo.Application application;
@Before
public void before() throws NoSuchFieldException {
application = new SpringCloudStreamAppGeneratorMojo.Application();
application.setName("log");
application.setType(AppDefinition.AppType.sink);
application.setVersion("3.0.0.BUILD-SNAPSHOT");
application.setConfigClass("io.pivotal.java.function.log.consumer.LogConsumerConfiguration.class");
application.getContainerImage().setFormat(AppDefinition.ContainerImageFormat.Docker);
application.getContainerImage().setBaseImage("base/image");
application.getMetadata().getSourceTypeFilters().add("io.pivotal.java.function.log.consumer.LogConsumerProperties");
application.getMetadata().getNameFilters().add("server.port");
application.getProperties().put("spring.cloud.streamapp.security.enabled", "false");
application.getProperties().put("spring.cloud.streamapp.security.csrf-enabled", "false");
Dependency dep = new Dependency();
dep.setGroupId("io.pivotal.java.function");
dep.setArtifactId("log-consumer");
dep.setVersion("1.0.0.BUILD-SNAPSHOT");
application.getMaven().getDependencies().add(dep);
// BOM
application.setBootVersion("2.3.0.M1");
application.getMetadata().setMavenPluginVersion("1.0.2.BUILD-SNAPSHOT");
setMojoProperty("application", application);
//Binders
SpringCloudStreamAppGeneratorMojo.Binder kafkaBinder = new SpringCloudStreamAppGeneratorMojo.Binder();
Dependency kafkaDep = new Dependency();
kafkaDep.setGroupId("org.springframework.cloud");
kafkaDep.setArtifactId("spring-cloud-stream-binder-kafka");
kafkaBinder.getMaven().getDependencies().add(kafkaDep);
SpringCloudStreamAppGeneratorMojo.Binder rabbitBinder = new SpringCloudStreamAppGeneratorMojo.Binder();
Dependency rabbitDep = new Dependency();
rabbitDep.setGroupId("org.springframework.cloud");
rabbitDep.setArtifactId("spring-cloud-stream-binder-rabbit");
rabbitBinder.getMaven().getDependencies().add(rabbitDep);
Map<String, SpringCloudStreamAppGeneratorMojo.Binder> binders = new HashMap<>();
binders.put("kafka", kafkaBinder);
binders.put("rabbit", rabbitBinder);
setMojoProperty("binders", binders);
//setMojoProperty("generatedProjectHome", "./target/apps");
setMojoProperty("generatedProjectHome", projectHome.getRoot().getAbsolutePath());
}
@Test
public void testWithDisabledContainerMetadata() throws Exception {
// disable metadata in container image (Default)
application.getContainerImage().setEnableMetadata(false);
springCloudStreamAppMojo.execute();
//Model pomModel = getModel(new File("./target/apps"));
Model pomModel = getModel(new File(projectHome.getRoot().getAbsolutePath()));
List<Plugin> plugins = pomModel.getBuild().getPlugins();
// The properties-maven-plugin should not be defined if the container metadata is not enabled.
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("properties-maven-plugin")).count()).isEqualTo(0);
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).count()).isEqualTo(1);
Plugin jibPlugin = plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).findFirst().get();
assertThat(jibPlugin.getConfiguration().toString())
.doesNotContain("<org.springframework.cloud.dataflow.spring-configuration-metadata.json>" +
"${org.springframework.cloud.dataflow.spring.configuration.metadata.json}" +
"</org.springframework.cloud.dataflow.spring-configuration-metadata.json>");
}
@Test
public void testDefaultProjectCreationByPlugin() throws Exception {
// Enable Metadata in Container Image!
application.getContainerImage().setEnableMetadata(true);
springCloudStreamAppMojo.execute();
//assertGeneratedPomXml(new File("./target/apps"));
assertGeneratedPomXml(new File(projectHome.getRoot().getAbsolutePath()));
}
@Test
public void testCustomBootMavenPluginConfiguration() throws Exception {
application.setBootPluginConfiguration("<![CDATA[\n" +
" <requiresUnpack>\n" +
" <dependency>\n" +
" <groupId>org.python</groupId>\n" +
" <artifactId>jython-standalone</artifactId>\n" +
" </dependency>\n" +
" </requiresUnpack>\n" +
" ]]>");
springCloudStreamAppMojo.execute();
Model pomModel = getModel(new File(projectHome.getRoot().getAbsolutePath()));
List<Plugin> plugins = pomModel.getBuild().getPlugins();
final Optional<Plugin> bootPlugin = plugins.stream().filter(p -> p.getArtifactId().equals("spring-boot-maven-plugin")).findFirst();
assertThat(bootPlugin.isPresent()).isTrue();
final Plugin plugin = bootPlugin.get();
final Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
assertThat(configuration.getValue().contains("<requiresUnpack>")).isTrue();
assertThat(configuration.getValue().contains("jython-standalone")).isTrue();
assertThat(configuration.getValue().contains("</requiresUnpack>")).isTrue();
}
private void assertGeneratedPomXml(File rootPath) {
Model pomModel = getModel(rootPath);
List<Dependency> dependencies = pomModel.getDependencies();
assertThat(dependencies.size()).isEqualTo(3);
assertThat(dependencies.stream()
.filter(d -> d.getArtifactId().equals("log-consumer")).count()).isEqualTo(1);
assertThat(dependencies.stream()
.filter(d -> d.getArtifactId().equals("spring-cloud-stream-binder-kafka")).count()).isEqualTo(1);
Parent parent = pomModel.getParent();
assertThat(parent.getArtifactId()).isEqualTo("spring-boot-starter-parent");
assertThat(parent.getVersion()).isEqualTo("2.3.0.M1");
assertThat(pomModel.getArtifactId()).isEqualTo("log-sink-kafka");
assertThat(pomModel.getGroupId()).isEqualTo("org.springframework.cloud.stream.app");
assertThat(pomModel.getName()).isEqualTo("log-sink-kafka");
assertThat(pomModel.getVersion()).isEqualTo("3.0.0.BUILD-SNAPSHOT");
assertThat(pomModel.getDescription()).isEqualTo("Spring Cloud Stream Log Sink Kafka Binder Application");
List<Plugin> plugins = pomModel.getBuild().getPlugins();
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("spring-boot-maven-plugin")).count()).isEqualTo(1);
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("properties-maven-plugin")).count()).isEqualTo(1);
assertThat(plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).count()).isEqualTo(1);
Plugin jibPlugin = plugins.stream().filter(p -> p.getArtifactId().equals("jib-maven-plugin")).findFirst().get();
assertThat(jibPlugin.getConfiguration().toString())
.contains("<org.springframework.cloud.dataflow.spring-configuration-metadata.json>" +
"${org.springframework.cloud.dataflow.spring.configuration.metadata.json}" +
"</org.springframework.cloud.dataflow.spring-configuration-metadata.json>");
assertThat(jibPlugin.getConfiguration().toString()).contains("<image>base/image</image>");
assertThat(pomModel.getRepositories().size()).isEqualTo(2);
}
private Model getModel(File rootPath) {
File pomXml = new File(new File(rootPath, "log-sink-kafka"), "pom.xml");
try (InputStream is = new FileInputStream(pomXml)) {
return new MavenXpp3Reader().read(is);
}
catch (IOException | XmlPullParserException e) {
throw new IllegalStateException(e);
}
}
private void setMojoProperty(String propertyName, Object value) throws NoSuchFieldException {
Field mojoProperty = mojoClazz.getDeclaredField(propertyName);
mojoProperty.setAccessible(true);
ReflectionUtils.setField(mojoProperty, springCloudStreamAppMojo, value);
}
}

View File

@@ -0,0 +1,302 @@
<?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>http-source-apps</artifactId>
<version>3.0.0.BUILD-SNAPSHOT</version>
<name>http-source-apps</name>
<description>http source apps</description>
<packaging>jar</packaging>
<properties>
<spring-boot.version>2.3.0.M1</spring-boot.version>
<stream-apps-core.version>3.0.0.BUILD-SNAPSHOT</stream-apps-core.version>
<java-cfenv-boot.version>2.1.1.RELEASE</java-cfenv-boot.version>
<app-metadata-maven-plugin-version>1.0.2.BUILD-SNAPSHOT</app-metadata-maven-plugin-version>
<!-- Emulate the properties that otherwise should come from the stream-apps-parent parent -->
<java-functions.version>1.0.0.BUILD-SNAPSHOT</java-functions.version>
<spring-cloud-dependencies.version>Hoxton.RELEASE</spring-cloud-dependencies.version>
<spring-cloud-stream-dependencies.version>Horsham.SR2</spring-cloud-stream-dependencies.version>
<spring-cloud-function-dependencies.version>3.0.2.RELEASE</spring-cloud-function-dependencies.version>
<apps.base-image>springcloud/baseimage:1.0.4</apps.base-image>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<executions>
<execution>
<id>app-gen</id>
<phase>package</phase>
<goals>
<goal>generate-app</goal>
</goals>
</execution>
</executions>
<configuration>
<global>
<application>
<bootVersion>${spring-boot.version}</bootVersion>
<metadata>
<mavenPluginVersion>2.0.2.BUILD-SNAPSHOT</mavenPluginVersion>
</metadata>
<containerImage>
<orgName>globalOrgName</orgName>
<baseImage>globalBaseImage</baseImage>
<enableMetadata>true</enableMetadata>
</containerImage>
<properties>
<appProperty1>value1</appProperty1>
<appProperty2>value2</appProperty2>
</properties>
<maven>
<properties>
<global.maven.prop1>value1</global.maven.prop1>
<global.maven.prop2>value2</global.maven.prop2>
</properties>
<dependencyManagement>
<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>
</dependencyManagement>
<dependencies>
<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>org.springframework.cloud.stream.app</groupId>
<artifactId>stream-applications-postprocessor-common</artifactId>
<version>3.0.0-SNAPSHOT</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</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>
</dependencies>
<plugins>
<plugin>
<groupId>org.logaritex</groupId>
<artifactId>test-additional-maven-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<![CDATA[
<from>
<image>${apps.base-image}</image>
</from>
<to>
<image>springcloudstream:${project.artifactId}:3.0.0.BUILD-SNAPSHOT</image>
</to>
<container>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<format>Docker</format>
<labels>
<org.springframework.cloud.dataflow.spring-configuration-metadata.json>${org.springframework.cloud.dataflow.spring.configuration.metadata.json}</org.springframework.cloud.dataflow.spring-configuration-metadata.json>
</labels>
</container>
]]>
</configuration>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-global</id>
<name>bintray-global</name>
<url>https://dl.bintray.com/globbals/maven</url>
</repository>
</repositories>
</maven>
</application>
<binders>
<kafka>
<maven>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-binder</id>
<name>bintray-binder</name>
<url>https://dl.bintray.com/binder/maven</url>
</repository>
</repositories>
</maven>
</kafka>
<rabbit>
<maven>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
</dependencies>
</maven>
</rabbit>
</binders>
</global>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-generator-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<configuration>
<generatedProjectHome>./target/apps</generatedProjectHome>
<application>
<name>http</name>
<type>source</type>
<version>${project.version}</version>
<configClass>io.pivotal.java.function.http.supplier.HttpSupplierConfiguration.class</configClass>
<groupId>org.springframework.cloud.stream.app.test</groupId>
<properties>
<spring.main.web-application-type>reactive</spring.main.web-application-type>
<spring.cloud.streamapp.security.enabled>false</spring.cloud.streamapp.security.enabled>
<spring.cloud.streamapp.security.csrf-enabled>false</spring.cloud.streamapp.security.csrf-enabled>
<!-- override global property -->
<appProperty2>value2Updated</appProperty2>
</properties>
<containerImage>
<orgName>testspringcloud</orgName>
<enableMetadata>true</enableMetadata>
</containerImage>
<maven>
<properties>
<random.maven.prop>value1</random.maven.prop>
</properties>
<dependencies>
<dependency>
<groupId>io.pivotal.java.function</groupId>
<artifactId>http-supplier</artifactId>
<version>${java-functions.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bintray-application</id>
<name>bintray-app</name>
<url>https://dl.bintray.com/app/maven</url>
</repository>
</repositories>
</maven>
<metadata>
<sourceTypeFilters>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties</filter>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors</filter>
</sourceTypeFilters>
<nameFilters>
<filter>server.port</filter>
</nameFilters>
</metadata>
</application>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-app-starter-metadata-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<configuration>
<storeFilteredMetadata>true</storeFilteredMetadata>
<metadataFilter>
<names>
<filter>server.port</filter>
</names>
<sourceTypes>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties</filter>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors</filter>
</sourceTypes>
</metadataFilter>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,95 @@
= Spring Cloud Stream & Task Maven Metadata Plugin
Maven plugin for aggregating Spring Boot metadata from all dependencies of a project into
a single metadata-only artifact.
== Usage
To use this plugin, configure this plugin for your app project (either directly or through a parent POM):
```
<build>
<plugins>
<plugin>
<groupId>org.springframework.cloud.stream.app.plugin</groupId>
<artifactId>spring-cloud-app-starter-metadata-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<executions>
<execution>
<id>aggregate-metadata</id>
<phase>compile</phase>
<goals>
<goal>aggregate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
This will produce and attach to the build a jar file named `<artifactId>-<version>-metadata.jar` (`metadata` being the
maven classifier used for the artifact, which can be customized as such:)
```
<build>
<plugins>
<plugin>
<groupId>org.springframework.cloud.stream.app.plugin</groupId>
<artifactId>spring-cloud-app-starter-metadata-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<executions>
<execution>
<id>aggregate-metadata</id>
<phase>compile</phase>
<goals>
<goal>aggregate-metadata</goal>
</goals>
<configuration>
<classifier>foobar</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
You can filter in subset of the gathered metadata and store it into a java property file: `META-INF/spring-configuration-metadata-encoded.properties`.
This file has single property `org.springframework.cloud.dataflow.spring.configuration.metadata.json` which contains the pre-filtered metadata encoded as Base64.
To activate this feature you need to set the `storeFilteredMetadata` parameter to `true`. Use the `metadataFilter` to configure the whitelisted metadata content to include.
For example:
```
<build>
<plugins>
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-app-starter-metadata-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<configuration>
<storeFilteredMetadata>true</storeFilteredMetadata>
<metadataFilter>
<names>
<filter>server.port</filter>
</names>
<sourceTypes>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties</filter>
<filter>io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors</filter>
</sourceTypes>
</metadataFilter>
</configuration>
<executions>
<execution>
<id>aggregate-metadata</id>
<phase>compile</phase>
<goals>
<goal>aggregate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```

View File

@@ -0,0 +1,150 @@
<?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>spring-cloud-dataflow-apps-metadata-plugin</artifactId>
<packaging>maven-plugin</packaging>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-apps-plugin-parent</artifactId>
<version>1.0.8-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<maven.version>3.6.3</maven.version>
<junit.version>4.12</junit.version>
<assertj.version>3.11.1</assertj.version>
<commons-text.version>1.8</commons-text.version>
<spring.version>5.2.8.RELEASE</spring.version>
<snakeyaml.version>1.26</snakeyaml.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>io.rsocket</groupId>
<artifactId>rsocket-core</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons-text.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,625 @@
/*
* Copyright 2017-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.dataflow.app.plugin;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationprocessor.metadata.ItemHint;
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.boot.configurationprocessor.metadata.ItemHint.ValueHint;
import static org.springframework.boot.configurationprocessor.metadata.ItemHint.ValueProvider;
/**
* A maven plugin that will gather all Spring Boot metadata files from all transitive dependencies and will aggregate
* them in one metadata-only artifact.
*
* @author Eric Bottard
* @author David Turanski
* @author Christian Tzolov
* @author Ilayaperumal Gopinathan
*/
@Mojo(
name = "aggregate-metadata",
requiresDependencyResolution = ResolutionScope.RUNTIME,
defaultPhase = LifecyclePhase.COMPILE
)
public class MetadataAggregationMojo extends AbstractMojo {
static final String METADATA_PATH = "META-INF/spring-configuration-metadata.json";
static final String VISIBLE_PROPERTIES_PATH = "META-INF/dataflow-configuration-metadata.properties";
static final String DEPRECATED_WHITELIST_PATH = "META-INF/dataflow-configuration-metadata-whitelist.properties";
static final String DEPRECATED_BACKUP_WHITELIST_PATH = "META-INF/spring-configuration-metadata-whitelist.properties";
static final String CONFIGURATION_PROPERTIES_CLASSES = "configuration-properties.classes";
static final String CONFIGURATION_PROPERTIES_NAMES = "configuration-properties.names";
static final String CONFIGURATION_PROPERTIES_INBOUND_PORTS = "configuration-properties.inbound-ports";
static final String CONFIGURATION_PROPERTIES_OUTBOUND_PORTS = "configuration-properties.outbound-ports";
static final String SPRING_CLOUD_FUNCTION_DEFINITION = "spring.cloud.function.definition";
static final String SPRING_CLOUD_STREAM_FUNCTION_DEFINITION = "spring.cloud.stream.function.definition";
static final String SPRING_CLOUD_STREAM_FUNCTION_BINDINGS = "spring.cloud.stream.function.bindings";
static final String SPRING_CLOUD_DATAFLOW_PORT_MAPPING_PROPERTIES = "dataflow-configuration-port-mapping.properties";
static final String SPRING_CLOUD_DATAFLOW_OPTION_GROUPS_PROPERTIES = "dataflow-configuration-option-groups.properties";
@Parameter(defaultValue = "${project}")
private MavenProject mavenProject;
@Parameter(defaultValue = "metadata")
private String classifier;
@Component
private MavenProjectHelper projectHelper;
@Parameter
private boolean storeFilteredMetadata;
@Parameter
private MetadataFilter metadataFilter;
private final JsonMarshaller jsonMarshaller = new JsonMarshaller();
public void execute() throws MojoExecutionException {
Result result = new Result(gatherConfigurationMetadata(null), gatherVisibleMetadata());
produceArtifact(result);
if (storeFilteredMetadata) {
getLog().debug("propertyClassFilter: " + metadataFilter);
if (metadataFilter == null) {
metadataFilter = new MetadataFilter();
}
if (result.visible.containsKey(CONFIGURATION_PROPERTIES_CLASSES)) {
String[] sourceTypes = result.visible.getProperty(CONFIGURATION_PROPERTIES_CLASSES, "").split(",");
if (sourceTypes != null && sourceTypes.length > 0) {
if (metadataFilter.getSourceTypes() == null) {
metadataFilter.setSourceTypes(new ArrayList<>());
}
for (String sourceType : sourceTypes) {
sourceType = sourceType.trim();
if (!metadataFilter.getSourceTypes().contains(sourceType)) {
metadataFilter.getSourceTypes().add(sourceType);
}
}
}
}
if (result.visible.containsKey(CONFIGURATION_PROPERTIES_NAMES)) {
String[] names = result.visible.getProperty(CONFIGURATION_PROPERTIES_NAMES, "").split(",");
if (names != null && names.length > 0) {
if (metadataFilter.getNames() == null) {
metadataFilter.setNames(new ArrayList<>());
}
for (String name : names) {
name = name.trim();
if (!metadataFilter.getNames().contains(name)) {
metadataFilter.getNames().add(name);
}
}
}
}
if (result.visible.containsKey(CONFIGURATION_PROPERTIES_CLASSES)) {
String[] sourceTypes = result.visible.getProperty(CONFIGURATION_PROPERTIES_CLASSES, "").split(",");
metadataFilter.getSourceTypes().addAll(Arrays.asList(sourceTypes));
}
storeFilteredMetadata();
}
//Add port mapping configuration based on the application configuration.
storeInboundOutboundPortMappingConfigurations(result.getPortMappingProperties());
}
/**
* Store pre-filtered and json-escaped metadata into a property file.
*/
private void storeFilteredMetadata() throws MojoExecutionException {
File projectMetaInfFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF");
if (!projectMetaInfFolder.exists()) {
if (!projectMetaInfFolder.mkdir()) {
throw new MojoExecutionException("Error creating META-INF folder for port mapping file!");
}
}
try (FileWriter fileWriter = new FileWriter(
new File(projectMetaInfFolder, "spring-configuration-metadata-encoded.properties"))) {
ConfigurationMetadata metadata = gatherConfigurationMetadata(metadataFilter);
String escapedJson = StringEscapeUtils.escapeJson(toJson(metadata));
fileWriter.write("org.springframework.cloud.dataflow.spring.configuration.metadata.json=" + escapedJson);
}
catch (IOException e) {
throw new MojoExecutionException("Error creating file ", e);
}
}
private void storeInboundOutboundPortMappingConfigurations(Properties properties) throws MojoExecutionException {
File projectMetaInfFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF");
if (!projectMetaInfFolder.exists()) {
if (!projectMetaInfFolder.mkdir()) {
throw new MojoExecutionException("Error creating META-INF folder for port mapping file!");
}
}
try (FileWriter fileWriter = new FileWriter(
new File(projectMetaInfFolder, SPRING_CLOUD_DATAFLOW_PORT_MAPPING_PROPERTIES))) {
properties.store(fileWriter, "Spring Cloud DataFlow Port Mapping");
}
catch (IOException e) {
throw new MojoExecutionException("Error creating file ", e);
}
}
private String toJson(ConfigurationMetadata metadata) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
jsonMarshaller.write(metadata, baos);
String json = baos.toString();
// Hack to workaround the https://github.com/mojohaus/properties-maven-plugin/issues/27 and
// https://github.com/mojohaus/properties-maven-plugin/pull/38 properties-maven-plugin issues.
json = json.replaceAll("\\$\\{", "{");
return json;
}
/**
* Read all existing metadata from this project runtime dependencies and merge them in a single object.
*/
/*default*/ Properties gatherVisibleMetadata() throws MojoExecutionException {
Properties visible = new Properties();
List<String> inboundPorts = new ArrayList<>();
List<String> outboundPorts = new ArrayList<>();
try {
for (String path : mavenProject.getRuntimeClasspathElements()) {
if (Files.isDirectory(Paths.get(path))) {
for (String visibleProperties : new String[] { VISIBLE_PROPERTIES_PATH, DEPRECATED_WHITELIST_PATH,
DEPRECATED_BACKUP_WHITELIST_PATH }) {
Optional<Properties> properties;
properties = getVisibleFromFile(Paths.get(path, visibleProperties));
if (properties.isPresent()) {
if (!visibleProperties.equals(VISIBLE_PROPERTIES_PATH)) {
getLog().warn("Use of " + visibleProperties + " is deprecated." +
" Please use " + VISIBLE_PROPERTIES_PATH);
}
visible = properties.get();
break;
}
}
File dir = new File(path);
for (File file : dir.listFiles()) {
Properties properties = new Properties();
if (file.isFile() && file.canRead() && file.getName().endsWith(".properties")) {
try (InputStream is = new FileInputStream(file)) {
properties.load(is);
}
}
if (file.isFile() && file.canRead() && (file.getName().endsWith(".yaml") || file.getName()
.endsWith(".yml"))) {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(new FileSystemResource(file));
properties = yamlPropertiesFactoryBean.getObject();
}
if (!properties.isEmpty()) {
String functionDefinitions = null;
if (properties.containsKey(SPRING_CLOUD_FUNCTION_DEFINITION)) {
functionDefinitions = properties.getProperty(SPRING_CLOUD_FUNCTION_DEFINITION);
}
else if (properties.containsKey(SPRING_CLOUD_STREAM_FUNCTION_DEFINITION)) {
functionDefinitions = properties
.getProperty(SPRING_CLOUD_STREAM_FUNCTION_DEFINITION);
}
for (String functionDefinition : StringUtils
.delimitedListToStringArray(functionDefinitions, ";")) {
if (functionDefinition != null) {
for (Object propertyKey : properties.keySet()) {
if (((String) propertyKey).startsWith(
String.format("%s.%s-in-", SPRING_CLOUD_STREAM_FUNCTION_BINDINGS,
functionDefinition))) {
inboundPorts.add(properties.getProperty((String) propertyKey));
}
if (((String) propertyKey).startsWith(
String.format("%s.%s-out-", SPRING_CLOUD_STREAM_FUNCTION_BINDINGS,
functionDefinition))) {
outboundPorts.add(properties.getProperty((String) propertyKey));
}
}
}
}
}
}
}
else {
try (ZipFile zipFile = new ZipFile(new File(path))) {
ZipEntry entry;
for (String zipEntry : new String[] { VISIBLE_PROPERTIES_PATH, DEPRECATED_WHITELIST_PATH,
DEPRECATED_BACKUP_WHITELIST_PATH }) {
entry = zipFile.getEntry(zipEntry);
if (entry != null) {
if (!zipEntry.equals(VISIBLE_PROPERTIES_PATH)) {
getLog().warn("Use of " + zipEntry + " is deprecated." +
" Please use " + VISIBLE_PROPERTIES_PATH);
}
visible = getVisibleFromZipFile(visible, path, zipFile, entry);
break;
}
}
}
}
}
}
catch (Exception e) {
throw new MojoExecutionException("Exception trying to read metadata from dependencies of project", e);
}
if (!inboundPorts.isEmpty()) {
visible.put(CONFIGURATION_PROPERTIES_INBOUND_PORTS,
StringUtils.arrayToCommaDelimitedString(inboundPorts.toArray(new String[0])));
}
if (!outboundPorts.isEmpty()) {
visible.put(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS,
StringUtils.arrayToCommaDelimitedString(outboundPorts.toArray(new String[0])));
}
return visible;
}
/*default*/ ConfigurationMetadata gatherConfigurationMetadata(MetadataFilter metadataFilters)
throws MojoExecutionException {
ConfigurationMetadata metadata = new ConfigurationMetadata();
try {
for (String path : mavenProject.getRuntimeClasspathElements()) {
File file = new File(path);
if (file.isDirectory()) {
File localMetadata = new File(file, METADATA_PATH);
if (localMetadata.canRead()) {
try (InputStream is = new FileInputStream(localMetadata)) {
ConfigurationMetadata depMetadata = jsonMarshaller.read(is);
depMetadata = filterMetadata(depMetadata, metadataFilters);
getLog().debug("Merging metadata from " + path);
addEnumHints(depMetadata, getClassLoader(path));
metadata.merge(depMetadata);
}
}
}
else {
try (ZipFile zipFile = new ZipFile(file)) {
ZipEntry entry = zipFile.getEntry(METADATA_PATH);
if (entry != null) {
try (InputStream inputStream = zipFile.getInputStream(entry)) {
ConfigurationMetadata depMetadata = jsonMarshaller.read(inputStream);
depMetadata = filterMetadata(depMetadata, metadataFilters);
getLog().debug("Merging metadata from " + path);
addEnumHints(depMetadata, getClassLoader(path));
metadata.merge(depMetadata);
}
}
}
}
// Replace all escaped double quotes by a single one.
metadata.getItems().stream().forEach(itemMetadata -> {
if (!StringUtils.isEmpty(itemMetadata.getDescription()) && itemMetadata.getDescription()
.contains("\"")) {
itemMetadata.setDescription(itemMetadata.getDescription().replaceAll("\"", "'"));
}
});
}
}
catch (Exception e) {
throw new MojoExecutionException("Exception trying to read metadata from dependencies of project", e);
}
return metadata;
}
@SuppressWarnings("unchecked")
private ConfigurationMetadata filterMetadata(ConfigurationMetadata metadata, MetadataFilter metadataFilters) {
if (metadataFilters == null
|| (CollectionUtils.isEmpty(metadataFilters.getNames()) && CollectionUtils
.isEmpty(metadataFilters.getSourceTypes()))) {
return metadata; // nothing to filter by so take all;
}
List<String> sourceTypeFilters = CollectionUtils.isEmpty(metadataFilters.getSourceTypes()) ?
Collections.EMPTY_LIST : metadataFilters.getSourceTypes();
List<String> nameFilters = CollectionUtils.isEmpty(metadataFilters.getNames()) ?
Collections.EMPTY_LIST : metadataFilters.getNames();
ConfigurationMetadata filteredMetadata = new ConfigurationMetadata();
List<String> visibleNames = new ArrayList<>();
for (ItemMetadata itemMetadata : metadata.getItems()) {
String metadataName = itemMetadata.getName();
String metadataSourceType = itemMetadata.getSourceType();
if (StringUtils.hasText(metadataSourceType) && sourceTypeFilters.contains(metadataSourceType.trim())) {
filteredMetadata.add(itemMetadata);
visibleNames.add(itemMetadata.getName());
}
if (StringUtils.hasText(metadataName) && nameFilters.contains(metadataName.trim())) {
filteredMetadata.add(itemMetadata);
visibleNames.add(itemMetadata.getName());
}
}
// copy the hits only for the visible metadata.
for (ItemHint itemHint : metadata.getHints()) {
if (itemHint != null && visibleNames.contains(itemHint.getName())) {
filteredMetadata.add(itemHint);
}
}
return filteredMetadata;
}
private Properties getVisibleFromZipFile(Properties visible, String path, ZipFile zipFile, ZipEntry entry)
throws IOException {
try (InputStream inputStream = zipFile.getInputStream(entry)) {
getLog().debug("Merging visible metadata from " + path);
visible = merge(visible, inputStream);
}
return visible;
}
private Optional<Properties> getVisibleFromFile(Path visiblePropertiesPath) throws IOException {
File localVisible = visiblePropertiesPath.toFile();
if (localVisible.canRead()) {
Properties visible = new Properties();
try (InputStream is = new FileInputStream(localVisible)) {
getLog().debug("!!!! Merging visible metadata from " + visiblePropertiesPath.toString());
visible = merge(visible, is);
return Optional.of(visible);
}
}
return Optional.empty();
}
Properties merge(Properties visible, InputStream is) throws IOException {
Properties mergedProperties = new Properties();
mergedProperties.load(is);
if (!mergedProperties.containsKey(CONFIGURATION_PROPERTIES_CLASSES) && !mergedProperties
.containsKey(CONFIGURATION_PROPERTIES_NAMES)) {
getLog().warn(String.format("Visible properties does not contain any required keys: %s",
StringUtils.arrayToCommaDelimitedString(new String[] {
CONFIGURATION_PROPERTIES_CLASSES,
CONFIGURATION_PROPERTIES_NAMES
})));
return visible;
}
if (!CollectionUtils.isEmpty(visible)) {
mergeCommaDelimitedValue(visible, mergedProperties, CONFIGURATION_PROPERTIES_CLASSES);
mergeCommaDelimitedValue(visible, mergedProperties, CONFIGURATION_PROPERTIES_NAMES);
}
return mergedProperties;
}
private void mergeCommaDelimitedValue(Properties currentProperties, Properties newProperties, String key) {
if (currentProperties.containsKey(key) || newProperties.containsKey(key)) {
Collection<String> values = StringUtils.commaDelimitedListToSet(currentProperties.getProperty(key));
values.addAll(StringUtils.commaDelimitedListToSet(newProperties.getProperty(key)));
if (newProperties.containsKey(key)) {
getLog().debug(String.format("Merging visible property %s=%s", key, newProperties.getProperty(key)));
}
newProperties.setProperty(key, StringUtils.collectionToCommaDelimitedString(values));
}
}
/**
* Create a jar file with the given metadata and "attach" it to the current maven project.
*/
/*default*/ void produceArtifact(Result result) throws MojoExecutionException {
String artifactLocation = String
.format("target/%s-%s-%s.jar", mavenProject.getArtifactId(), mavenProject.getVersion(), classifier);
File output = new File(mavenProject.getBasedir(), artifactLocation);
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(output))) {
ZipEntry entry = new ZipEntry(METADATA_PATH);
jos.putNextEntry(entry);
jsonMarshaller.write(result.metadata, jos);
entry = new ZipEntry(VISIBLE_PROPERTIES_PATH);
jos.putNextEntry(entry);
result.visible.store(jos, "Describes visible properties for this app");
entry = new ZipEntry(DEPRECATED_WHITELIST_PATH);
jos.putNextEntry(entry);
result.visible.store(jos, "DEPRECATED: Describes visible properties for this app");
entry = new ZipEntry(DEPRECATED_BACKUP_WHITELIST_PATH);
jos.putNextEntry(entry);
result.visible.store(jos, "DEPRECATED: Describes visible properties for this app");
entry = new ZipEntry("META-INF/" + SPRING_CLOUD_DATAFLOW_PORT_MAPPING_PROPERTIES);
jos.putNextEntry(entry);
entry = new ZipEntry("META-INF/" + SPRING_CLOUD_DATAFLOW_OPTION_GROUPS_PROPERTIES);
jos.putNextEntry(entry);
result.getPortMappingProperties().store(jos, "Describes visible port mapping properties for this app");
getLog().info(String.format("Attaching %s to current project", output.getCanonicalPath()));
projectHelper.attachArtifact(mavenProject, output, classifier);
}
catch (IOException e) {
throw new MojoExecutionException("Error writing to file", e);
}
}
void addEnumHints(ConfigurationMetadata configurationMetadata, ClassLoader classLoader) {
Map<String, List<ValueProvider>> providers = new HashMap<>();
Map<String, ItemHint> itemHints = new HashMap<>();
for (ItemMetadata property : configurationMetadata.getItems()) {
if (property.isOfItemType(ItemMetadata.ItemType.PROPERTY)) {
if (ClassUtils.isPresent(property.getType(), classLoader)) {
Class<?> clazz = ClassUtils.resolveClassName(property.getType(), classLoader);
if (clazz.isEnum()) {
List<ValueHint> valueHints = new ArrayList<>();
for (Object o : clazz.getEnumConstants()) {
valueHints.add(new ValueHint(o, null));
}
if (!providers.containsKey(property.getType())) {
providers.put(property.getType(), new ArrayList<ValueProvider>());
}
//Equals is not correct for ValueProvider
boolean found = false;
for (ValueProvider valueProvider : providers.get(property.getType())) {
if (valueProvider.getName().equals(property.getType())) {
found = true;
}
}
if (!found) {
providers.get(property.getType()).add(new ValueProvider(property.getType(), null));
}
itemHints.put(property.getType(), new ItemHint(property.getName(), valueHints,
new ArrayList<>(providers.get(property.getType()))));
}
}
}
}
if (!CollectionUtils.isEmpty(itemHints)) {
for (ItemHint itemHint : itemHints.values()) {
configurationMetadata.add(itemHint);
}
}
}
private ClassLoader getClassLoader(String jarPath) {
ClassLoader classLoader = null;
try {
classLoader = new URLClassLoader(new URL[] { new URL("file://" + jarPath) },
this.getClass().getClassLoader());
}
catch (MalformedURLException e) {
// pass through
}
return classLoader;
}
public static class MetadataFilter {
private List<String> names;
private List<String> sourceTypes;
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
public List<String> getSourceTypes() {
return sourceTypes;
}
public void setSourceTypes(List<String> sourceTypes) {
this.sourceTypes = sourceTypes;
}
@Override
public String toString() {
return "MetadataFilter{" +
"name=" + names +
", sourceType=" + sourceTypes +
'}';
}
}
/**
* A tuple holding both configuration metadata and the whitelist properties.
*
* @author Eric Bottard
*/
/*default*/ static final class Result {
private final ConfigurationMetadata metadata;
private final Properties visible;
private Properties getPortMappingProperties() {
Properties portMappingProperties = new Properties();
visible.entrySet().stream()
.filter(e -> e.getKey().equals(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS) || e.getKey().equals(CONFIGURATION_PROPERTIES_INBOUND_PORTS))
.forEach(e -> portMappingProperties.put(e.getKey(), e.getValue()));
return portMappingProperties;
}
private Result(ConfigurationMetadata metadata, Properties visible) {
this.metadata = metadata;
this.visible = visible;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2018-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.dataflow.app.plugin;
import org.junit.Test;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationprocessor.metadata.ItemHint;
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
import org.springframework.cloud.dataflow.completion.Expresso;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author David Turanski
**/
public class EnumHintProviderTest {
private JsonMarshaller jsonMarshaller = new JsonMarshaller();
@Test
public void test() throws Exception {
ConfigurationMetadata configurationMetadata = jsonMarshaller.read(new ClassPathResource("META-INF/spring"
+ "-configuration-metadata.json").getInputStream());
new MetadataAggregationMojo().addEnumHints(configurationMetadata, this.getClass().getClassLoader());
assertThat(configurationMetadata.getHints()).hasSize(1);
ItemHint itemHint = configurationMetadata.getHints().get(0);
// equals fails on ValueHint so ...
// assertThat(new ItemHint.ValueHint("SINGLE",null)).isEqualTo(new ItemHint.ValueHint("SINGLE",null));
assertThat(itemHint.getValues()).hasSize(2);
assertThat(itemHint.getValues().get(0).getValue()).isEqualTo(Expresso.SINGLE);
assertThat(itemHint.getValues().get(1).getValue()).isEqualTo(Expresso.DOUBLE);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2018-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.dataflow.app.plugin;
import java.io.IOException;
import java.util.Properties;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.dataflow.app.plugin.MetadataAggregationMojo.CONFIGURATION_PROPERTIES_CLASSES;
import static org.springframework.cloud.dataflow.app.plugin.MetadataAggregationMojo.CONFIGURATION_PROPERTIES_NAMES;
/**
* @author David Turanski
**/
public class MergeWhitelistPropertiesTest {
@Test
public void merge() throws IOException {
Properties properties1 = new Properties();
properties1.load(new ClassPathResource("META-INF/whitelist-1.properties").getInputStream());
Properties properties2 = new MetadataAggregationMojo().merge(properties1,
new ClassPathResource("META-INF/whitelist-2.properties").getInputStream());
assertThat(properties2).containsKeys(CONFIGURATION_PROPERTIES_CLASSES, CONFIGURATION_PROPERTIES_NAMES);
}
@Test
public void mergeReverseOrder() throws IOException {
Properties properties1 = new Properties();
properties1.load(new ClassPathResource("META-INF/whitelist-2.properties").getInputStream());
Properties properties2 = new MetadataAggregationMojo().merge(properties1,
new ClassPathResource("META-INF/whitelist-1.properties").getInputStream());
assertThat(properties2).containsKeys(CONFIGURATION_PROPERTIES_CLASSES, CONFIGURATION_PROPERTIES_NAMES);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2018-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.dataflow.completion;
/**
* @author David Turanski
**/
public enum Expresso {
SINGLE,
DOUBLE;
}

View File

@@ -0,0 +1,26 @@
{
"groups": [
{
"name": "filter",
"type": "foo.bar.FilterProperties",
"sourceType": "foo.bar.FilterProperties"
}
],
"properties": [
{
"name": "filter.expression",
"type": "org.springframework.expression.Expression",
"description": "A predicate to evaluate",
"sourceType": "foo.bar.FilterProperties",
"defaultValue": "true"
},
{
"name": "filter.expresso",
"type": "org.springframework.cloud.dataflow.completion.Expresso",
"description": "A property of type enum and whose name starts like 'expression'",
"sourceType": "foo.bar.FilterProperties"
}
],
"hints": [
]
}

View File

@@ -0,0 +1,2 @@
configuration-properties.classes=\
org.springframework.cloud.stream.app.tasklaunchrequest.DataflowTaskLaunchRequestProperties

View File

@@ -0,0 +1,25 @@
configuration-properties.classes=\
org.springframework.cloud.stream.app.trigger.TriggerPropertiesMaxMessagesDefaultUnlimited
configuration-properties.names=\
sftp.auto-create-local-dir,\
sftp.delete-remote-files,\
sftp.directories,\
sftp.factories,\
sftp.factory.allow-unknown-keys,\
sftp.factory.cache-sessions,\
sftp.factory.host,\
sftp.factory.known-hosts-expression,\
sftp.factory.pass-phrase,\
sftp.factory.password,\
sftp.factory.port,\
sftp.factory.private-key,\
sftp.factory.username,\
sftp.fair,\
sftp.filename-pattern,\
sftp.filename-regex,\
sftp.list-only,\
sftp.local-dir,\
sftp.max-fetch,\
sftp.preserve-timestamp,\
sftp.remote-dir,\
sftp.remote-file-separator