From b9c1e1bccfa2a6f6d6268b7a139c70f2ca5cfb86 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 19 Aug 2020 16:03:00 -0400 Subject: [PATCH 01/76] Initial commit --- etc/checkstyle/checkstyle-header.txt | 17 ++ etc/checkstyle/checkstyle-suppressions.xml | 19 ++ etc/checkstyle/checkstyle.xml | 196 +++++++++++++++ etc/checkstyle/nohttp-checkstyle.xml | 29 +++ pom.xml | 272 +++++++++++++++++++++ 5 files changed, 533 insertions(+) create mode 100644 etc/checkstyle/checkstyle-header.txt create mode 100644 etc/checkstyle/checkstyle-suppressions.xml create mode 100644 etc/checkstyle/checkstyle.xml create mode 100644 etc/checkstyle/nohttp-checkstyle.xml create mode 100644 pom.xml diff --git a/etc/checkstyle/checkstyle-header.txt b/etc/checkstyle/checkstyle-header.txt new file mode 100644 index 00000000..39429ee6 --- /dev/null +++ b/etc/checkstyle/checkstyle-header.txt @@ -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$ +^$ +^.*$ diff --git a/etc/checkstyle/checkstyle-suppressions.xml b/etc/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 00000000..80da92e6 --- /dev/null +++ b/etc/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/etc/checkstyle/checkstyle.xml b/etc/checkstyle/checkstyle.xml new file mode 100644 index 00000000..c3e1de40 --- /dev/null +++ b/etc/checkstyle/checkstyle.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/checkstyle/nohttp-checkstyle.xml b/etc/checkstyle/nohttp-checkstyle.xml new file mode 100644 index 00000000..4e21a0bd --- /dev/null +++ b/etc/checkstyle/nohttp-checkstyle.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..36e67a95 --- /dev/null +++ b/pom.xml @@ -0,0 +1,272 @@ + + + 4.0.0 + org.springframework.cloud + spring-cloud-dataflow-apps-plugin-parent + 1.0.0-SNAPSHOT + spring-cloud-dataflow-apps-plugin-parent + Spring Cloud Dataflow Apps Plugin Parent + pom + + + 1.8 + 3.1.1 + 3.2.1 + 2.22.2 + UTF-8 + UTF-8 + ${java.version} + ${java.version} + 3.0.1 + 3.1.0 + false + true + true + true + 8.29 + https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow-apps-plugin/master/etc/checkstyle + + ${checkstyle.location}/checkstyle-suppressions.xml + + + ${checkstyle.location}/nohttp-checkstyle.xml + + + ${checkstyle.location}/checkstyle-suppressions.xml + + 0.0.2.RELEASE + true + 0.0.7 + + + + + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + 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. + + + + scm:git:git://github.com/spring-cloud/spring-cloud-dataflow-apps-plugin.git + scm:git:ssh://git@github.com/spring-cloud/spring-cloud-dataflow-apps-plugin.git + https://github.com/spring-cloud/spring-cloud-dataflow-apps-plugin + + + + + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + javadoc + package + + jar + + + + + true + + + + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + package + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + **/*Tests.java + **/*Test.java + + + **/Abstract*.java + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + com.puppycrawl.tools + checkstyle + ${puppycrawl-tools-checkstyle.version} + + + io.spring.javaformat + spring-javaformat-checkstyle + ${spring-javaformat-checkstyle.version} + + + io.spring.nohttp + nohttp-checkstyle + ${nohttp-checkstyle.version} + + + + + checkstyle-validation + validate + true + + ${disable.checks} + ${checkstyle.location}/checkstyle.xml + ${checkstyle.location}/checkstyle-header.txt + + checkstyle.build.directory=${project.build.directory} + checkstyle.suppressions.file=${checkstyle.suppressions.file} + checkstyle.additional.suppressions.file=${checkstyle.additional.suppressions.file} + + true + + ${maven-checkstyle-plugin.includeTestSourceDirectory} + + ${maven-checkstyle-plugin.failsOnError} + + + ${maven-checkstyle-plugin.failOnViolation} + + + + check + + + + no-http-checkstyle-validation + validate + true + + ${disable.nohttp.checks} + ${checkstyle.nohttp.file} + **/* + **/.idea/**/*,**/.git/**/*,**/target/**/*,**/*.log + ./ + + + check + + + + + + + + + repo.spring.io + Spring Release Repository + https://repo.spring.io/libs-release-local + + + repo.spring.io + Spring Snapshot Repository + https://repo.spring.io/libs-snapshot-local + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + + + + + milestone + + + repo.spring.io + Spring Milestone Repository + https://repo.spring.io/libs-milestone-local + + + + + central + + + + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + + From 7a330dd6f9a2700abbf1b51f87b39aa5efd5e448 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 19 Aug 2020 16:45:31 -0400 Subject: [PATCH 02/76] Initial migration for the apps generator plugin Old location: https://github.com/spring-cloud/spring-cloud-app-starters-maven-plugins/tree/master/spring-cloud-stream-app-maven-plugin --- .gitignore | 27 ++ .mvn/jvm.config | 1 + .mvn/maven.config | 1 + .mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 2 + etc/checkstyle/checkstyle-suppressions.xml | 1 + mvnw | 253 ++++++++++++++ mvnw.cmd | 182 ++++++++++ pom.xml | 1 + .../LICENSE | 201 +++++++++++ .../README.adoc | 185 ++++++++++ .../mvnw | 253 ++++++++++++++ .../mvnw.cmd | 145 ++++++++ .../pom.xml | 157 +++++++++ .../dataflow/app/plugin/MavenXmlWriter.java | 153 ++++++++ .../SpringCloudStreamAppGeneratorMojo.java | 329 ++++++++++++++++++ .../dataflow/app/plugin/generator/AppBom.java | 57 +++ .../app/plugin/generator/AppDefinition.java | 184 ++++++++++ .../plugin/generator/ProjectGenerator.java | 221 ++++++++++++ .../generator/ProjectGeneratorProperties.java | 91 +++++ .../main/resources/template/.mvn/jvm.config | 1 + .../main/resources/template/.mvn/maven.config | 1 + .../.mvn/wrapper/MavenWrapperDownloader.java | 110 ++++++ .../template/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 48337 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../src/main/resources/template/App.java | 31 ++ .../src/main/resources/template/AppTests.java | 32 ++ .../src/main/resources/template/README.adoc | 1 + .../src/main/resources/template/app-pom.xml | 282 +++++++++++++++ .../main/resources/template/app.properties | 26 ++ .../resources/template/apps-container-pom.xml | 103 ++++++ .../src/main/resources/template/mvnw | 253 ++++++++++++++ .../src/main/resources/template/mvnw.cmd | 145 ++++++++ .../src/main/resources/templates/assembly.xml | 15 + .../dataflow/app/plugin/MojoHarnessTest.java | 115 ++++++ ...SpringCloudStreamAppGeneratorMojoTest.java | 191 ++++++++++ .../resources/unit/http-source-apps/pom.xml | 226 ++++++++++++ .../src/main/resources/test.txt | 1 + 39 files changed, 4095 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/jvm.config create mode 100644 .mvn/maven.config create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100755 mvnw create mode 100644 mvnw.cmd create mode 100644 spring-cloud-dataflow-apps-generator-plugin/LICENSE create mode 100644 spring-cloud-dataflow-apps-generator-plugin/README.adoc create mode 100755 spring-cloud-dataflow-apps-generator-plugin/mvnw create mode 100644 spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd create mode 100644 spring-cloud-dataflow-apps-generator-plugin/pom.xml create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/jvm.config create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/maven.config create mode 100755 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/MavenWrapperDownloader.java create mode 100755 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.jar create mode 100755 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.properties create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/App.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/AppTests.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/README.adoc create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-container-pom.xml create mode 100755 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw.cmd create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/templates/assembly.xml create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/test.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f5712a10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +apps/ +/application.yml +/application.properties +asciidoctor.css +*~ +.#* +*# +target/ +build/ +bin/ +_site/ +.classpath +.project +.settings +.springBeans +.DS_Store +*.sw* +*.iml +*.ipr +*.iws +.idea/ +.factorypath +spring-xd-samples/*/xd +dump.rdb +coverage-error.log +.apt_generated +aws.credentials.properties diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 00000000..0e7dabef --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1 @@ +-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom \ No newline at end of file diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 00000000..3b8cf46e --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..720797dc --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -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.5"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..0d5e649888a4843c1520054d9672f80c62ebbb48 GIT binary patch literal 50710 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�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 JS$+5G{{hr`vzY(@ literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..7d59a01f --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar diff --git a/etc/checkstyle/checkstyle-suppressions.xml b/etc/checkstyle/checkstyle-suppressions.xml index 80da92e6..9d3a0b2d 100644 --- a/etc/checkstyle/checkstyle-suppressions.xml +++ b/etc/checkstyle/checkstyle-suppressions.xml @@ -10,6 +10,7 @@ + diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..a08b219e --- /dev/null +++ b/mvnw @@ -0,0 +1,253 @@ +#!/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 '\!//' -e 's!.*$!!' ) +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 + echo Deactivating \"milestone\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') +fi + +if echo $VERSION | egrep -q 'RELEASE'; then + echo Activating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" +else + echo Deactivating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') +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} "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..b26ab24f --- /dev/null +++ b/mvnw.cmd @@ -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 Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing 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.5/maven-wrapper-0.5.5.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.5/maven-wrapper-0.5.5.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% diff --git a/pom.xml b/pom.xml index 36e67a95..abe7053c 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ + spring-cloud-dataflow-apps-generator-plugin diff --git a/spring-cloud-dataflow-apps-generator-plugin/LICENSE b/spring-cloud-dataflow-apps-generator-plugin/LICENSE new file mode 100644 index 00000000..9b259bdf --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/LICENSE @@ -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. diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc new file mode 100644 index 00000000..a5049b88 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -0,0 +1,185 @@ += spring-cloud-stream-app-maven-plugin +Maven plugin for generating spring cloud stream applications from the spring-cloud-stream-app-starters repository + +== Build + +mvn clean package [Requires JDK 8] + +== Sample Configuration for generating spring cloud stream apps + +[source, xml] +---- + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + 2.0.1.BUILD-SNAPSHOT + + 1.8 + ${spring-boot.version} + ${app-metadata-maven-plugin-version} + + + app-starters-core-dependencies + org.springframework.cloud.stream.app + app-starters-core-dependencies + ${app-starters-core-dependencies.version} + + + + + + + + counter + sink + ${project.version} + io.pivotal.java.function.counter.consumer.CounterConsumerConfiguration.class + counterConsumer + + + spring.cloud.stream.propagateOriginalContentType=false + + + + io.pivotal.java.function + counter-consumer + ${java-functions.version} + + + + + org.springframework.cloud.stream.app + app-starters-security-common + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud.stream.app + app-starters-micrometer-common + + + io.micrometer + micrometer-registry-influx + + + io.micrometer + micrometer-registry-prometheus + + + org.springframework.cloud + spring-cloud-cloudfoundry-connector + + + org.springframework.cloud + spring-cloud-spring-service-connector + + + io.micrometer.prometheus + prometheus-rsocket-spring + + + + +---- + +The bom for stream apps is specified in spring-cloud-stream-app-dependencies +(group id: org.springframework.cloud.stream.app) + +and for tasks it is spring-cloud-task-app-dependencies +(group id: org.springframework.cloud.task.app). + +Extra dependency management can be added as additional boms to the plugin using the property +-DbomsWithHigherPrecedence and value based on the pattern groupId:artfiactId:version. This can + be a comma separated collection of boms. Any bom added like this will get priority in the order + they are specified over any default values used in the plugin configuration. + + Spring Boot version used in the generated app can be overridden using the plugin property + -DbootVersion=. + + +==== Copy Third-Party Resources +The `copyResources` configuration block permits copying Resources from third-party jar dependencies into the +generated App Starter classpath. + +Form example if you want to copy a truststore and a self-signed certificate (`clientKeyStore.jks`, `cacerts`) bundled inside the +`org.springframework.cloud.stream.app:coap-app-starters-common:2.0.0.BUILD-SNAPSHOT` dependency you can add this to the +`spring-cloud-stream-app-maven-plugin` configuration: + +[source, xml] +---- + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + + ${session.executionRootDirectory}/apps + ${project.version} + .... + + + + + + org.springframework.cloud.stream.app + coap-app-starters-common + 2.0.0.BUILD-SNAPSHOT + clientKeyStore.jks,cacerts + + + + + +---- + +Later will add the following `maven-dependency-plugin` definition to the generated aps starter pom: + +[source, xml] +---- + + maven-dependency-plugin + + + + unpack + + + + + org.springframework.cloud.stream.app + coap-app-starters-common + 2.0.0.BUILD-SNAPSHOT + clientKeyStore.jks,cacerts + + + ${project.build.directory}/classes/ + + + + +---- + +This will ensure that the clientKeyStore.jks,cacerts files will be copied to the AppStarter's `BOOT-INF/classes/` + +==== Insert additional properties into the application.properties file for the created app. +The `additionalAppProperties` configuration block permits adding of additional properties to the application.properties file. +[source, xml] +---- + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + + ${session.executionRootDirectory}/apps + ${project.version} + .... + + + + + spring.cloud.task.closecontextEnabled=true + + + + +---- +In the example above we added the`spring.cloud.task.closecontextEnabled` property to the application.properties of the created app. diff --git a/spring-cloud-dataflow-apps-generator-plugin/mvnw b/spring-cloud-dataflow-apps-generator-plugin/mvnw new file mode 100755 index 00000000..a08b219e --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/mvnw @@ -0,0 +1,253 @@ +#!/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 '\!//' -e 's!.*$!!' ) +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 + echo Deactivating \"milestone\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') +fi + +if echo $VERSION | egrep -q 'RELEASE'; then + echo Activating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" +else + echo Deactivating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') +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} "$@" diff --git a/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd b/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd new file mode 100644 index 00000000..eb9a292a --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd @@ -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% diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml new file mode 100644 index 00000000..800d287d --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + + spring-cloud-dataflow-apps-generator-plugin + maven-plugin + spring-cloud-dataflow-apps-generator-plugin + + + org.springframework.cloud + spring-cloud-dataflow-apps-plugin-parent + 1.0.0-SNAPSHOT + .. + + + + UTF-8 + 3.6.3 + 3.6.0 + 1.15 + 5.2.8.RELEASE + + + + + com.samskivert + jmustache + ${jmustache.version} + + + + org.springframework + spring-core + ${spring.version} + + + + org.apache.maven + maven-plugin-api + provided + ${maven.version} + + + org.apache.maven + maven-core + ${maven.version} + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven.plugin.version} + provided + + + + junit + junit + 4.13 + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 3.3.0 + test + + + org.apache.maven + maven-compat + ${maven.version} + test + + + org.assertj + assertj-core + 3.15.0 + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven.plugin.version} + + generate-app + true + + + + mojo-descriptor + + descriptor + + + + help-goal + + helpmojo + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + run-its + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.2.1 + + true + ${project.build.directory}/it + + */pom.xml + + verify + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + test-compile + + + + + integration-test + + install + integration-test + verify + + + + + + + + + + + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java new file mode 100644 index 00000000..8385dc83 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java @@ -0,0 +1,153 @@ +/* + * 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). + *

+ * If above name conventions don't hold or additional processing is required implement a dedicated overload method. + * For example see {@link #toXml(Plugin)} + */ + public static 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. + *

+ * For Plugin Configuration you must nest the configuration content into a CDATA block like shown here: + * + * + * + * com.google.cloud.tools + * jib-maven-plugin + * 2.0.0 + * + * springcloud/openjdk + * + * springcloudstream:${project.artifactId} + * latest + * + * + * Docker + * + * ]]> + * + * + * + * + */ + public static String toXml(Plugin plugin) { + try { + Object configuration = plugin.getConfiguration(); + if (configuration != null && !(configuration instanceof Xpp3Dom)) { + plugin.setConfiguration(Xpp3DomBuilder.build(new StringReader( + "" + configuration.toString() + ""))); + } + } + catch (Exception ex) { + throw new IllegalStateException("Issue creating config for additional plugin", ex); + } + + return write(serializer -> invokeMavenXppWriteMethod( + plugin, "writePlugin", "plugin", serializer)); + } + + public static String write(Consumer 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 ) + } + 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 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 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); + } + } + + 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. + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java new file mode 100644 index 00000000..21c434d7 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -0,0 +1,329 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +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.plugin.AbstractMojo; +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.AppBom; +import org.springframework.cloud.dataflow.app.plugin.generator.AppDefinition; +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 + */ +@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 ContainerImage containerImage = new ContainerImage(); + + @Parameter(required = true) + private GeneratedApp generatedApp; + + @Parameter + List additionalAppProperties; + + @Parameter + List metadataSourceTypeFilters = new ArrayList<>(); + + @Parameter + List metadataNameFilters = new ArrayList<>(); + + @Parameter + List boms = new ArrayList<>(); + + @Parameter + List dependencies = new ArrayList<>(); + + @Parameter + List globalDependencies = new ArrayList<>(); + + @Parameter + List additionalPlugins = new ArrayList<>(); + + @Parameter + List binders = new ArrayList<>(); + + // Versions + @Parameter(defaultValue = "2.2.4.RELEASE", required = true) + private String bootVersion; + + @Parameter(defaultValue = "${app-metadata-maven-plugin-version}") + private String appMetadataMavenPluginVersion; + + @Override + public void execute() throws MojoFailureException { + // Bom + AppBom appBom = new AppBom() + .withSpringBootVersion(this.bootVersion) + .withAppMetadataMavenPluginVersion(this.appMetadataMavenPluginVersion); + + AppDefinition app = new AppDefinition(); + app.setName(this.generatedApp.getName()); + app.setType(this.generatedApp.getType()); + app.setVersion(this.generatedApp.getVersion()); + app.setConfigClass(this.generatedApp.getConfigClass()); + app.setFunctionDefinition(this.generatedApp.getFunctionDefinition()); + + this.populateVisiblePropertiesFromFile(this.metadataSourceTypeFilters, this.metadataNameFilters); + + if (!CollectionUtils.isEmpty(this.metadataSourceTypeFilters)) { + app.setMetadataSourceTypeFilters(this.metadataSourceTypeFilters); + } + + if (!CollectionUtils.isEmpty(this.metadataNameFilters)) { + app.setMetadataNameFilters(this.metadataNameFilters); + } + + app.setAdditionalProperties(this.additionalAppProperties); + + // BOM + app.setMavenManagedDependencies(this.boms.stream() + .filter(Objects::nonNull) + .map(dependency -> { + dependency.setScope("import"); + dependency.setType("pom"); + return dependency; + }) + .map(MavenXmlWriter::toXml) + .map(xml -> MavenXmlWriter.indent(xml, 12)) + .collect(Collectors.toList())); + + // Dependencies + List allDependenciesMerged = new ArrayList<>(this.dependencies); + allDependenciesMerged.addAll(this.globalDependencies); + app.setMavenDependencies(allDependenciesMerged.stream() + .map(MavenXmlWriter::toXml) + .map(xml -> MavenXmlWriter.indent(xml, 8)) + .collect(Collectors.toList())); + + // Plugins + app.setMavenPlugins(this.additionalPlugins.stream() + .map(MavenXmlWriter::toXml) + .map(d -> MavenXmlWriter.indent(d, 12)) + .collect(Collectors.toList())); + + // Container Image configuration + app.setContainerImageFormat(this.containerImage.getFormat()); + app.setEnableContainerImageMetadata(this.containerImage.isEnableMetadata()); + if (StringUtils.hasText(this.containerImage.getOrgName())) { + app.setContainerImageOrgName(this.containerImage.getOrgName()); + } + + app.setContainerImageTag(this.generatedApp.getVersion()); + + // Generator Properties + ProjectGeneratorProperties generatorProperties = new ProjectGeneratorProperties(); + generatorProperties.setBinders(this.binders); + generatorProperties.setOutputFolder(new File(this.generatedProjectHome)); + generatorProperties.setAppBom(appBom); + generatorProperties.setAppDefinition(app); + generatorProperties.setProjectResourcesDirectory(this.projectResourcesDir); + + try { + ProjectGenerator.getInstance().generate(generatorProperties); + } + catch (IOException e) { + throw new MojoFailureException("Project generation failure"); + } + } + + /** + * If the visible metadata properties file is provided in the source project, add + * its 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 sourceTypeFilters, List nameFilters) { + if (this.projectResourcesDir == null || !projectResourcesDir.exists()) { + return; + } + Optional 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 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 filterList) { + if (!StringUtils.isEmpty(csvFilterProperties)) { + for (String filterProperty : csvFilterProperties.trim().split(",")) { + if (StringUtils.hasText(filterProperty)) { + if (!filterList.contains(filterProperty.trim())) { + filterList.add(filterProperty.trim()); + } + } + } + } + } + + public static class GeneratedApp { + + private String name; + private String version; + private AppDefinition.AppType type; + private String configClass; + private String functionDefinition; + + 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 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); + } + } + + public static class ContainerImage { + private AppDefinition.ContainerImageFormat format = AppDefinition.ContainerImageFormat.Docker; + private String orgName = "springcloudstream"; + 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; + } + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java new file mode 100644 index 00000000..f5d55bf3 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * @author Christian Tzolov + */ +public class AppBom { + /** + * Spring Boot version to use. + */ + private String springBootVersion; + + private String appMetadataMavenPluginVersion; + + + public String getSpringBootVersion() { + return springBootVersion; + } + + public void setSpringBootVersion(String springBootVersion) { + this.springBootVersion = springBootVersion; + } + + public AppBom withSpringBootVersion(String springBootVersion) { + this.springBootVersion = springBootVersion; + return this; + } + + public String getAppMetadataMavenPluginVersion() { + return appMetadataMavenPluginVersion; + } + + public void setAppMetadataMavenPluginVersion(String appMetadataMavenPluginVersion) { + this.appMetadataMavenPluginVersion = appMetadataMavenPluginVersion; + } + + public AppBom withAppMetadataMavenPluginVersion(String appMetadataMavenPluginVersion) { + this.appMetadataMavenPluginVersion = appMetadataMavenPluginVersion; + return this; + } + +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java new file mode 100644 index 00000000..70c67102 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -0,0 +1,184 @@ +/* + * 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; + +/** + * @author Christian Tzolov + */ +public class AppDefinition { + + private String name; + private AppType type; + private String version; + private String configClass; + private String functionDefinition; + private List metadataSourceTypeFilters = new ArrayList<>(); + private List metadataNameFilters = new ArrayList<>(); + private List additionalProperties = new ArrayList<>(); + private List mavenManagedDependencies = new ArrayList<>(); + private List mavenDependencies = new ArrayList<>(); + private List mavenPlugins = new ArrayList<>(); + /** + * Allow to generate either Docker or OCI image formats + */ + private ContainerImageFormat containerImageFormat = ContainerImageFormat.Docker; + /** + * True will attempt to inline the (white) filtered Spring Boot metadata as Base64 encoded property. + */ + private boolean enableContainerImageMetadata = false; + private String containerImageOrgName = "springcloudstream"; + private String containerImageTag = "latest"; + + 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 List getAdditionalProperties() { + return additionalProperties; + } + + public void setAdditionalProperties(List additionalProperties) { + this.additionalProperties = additionalProperties; + } + + public List getMavenDependencies() { + return mavenDependencies; + } + + public void setMavenDependencies(List mavenDependencies) { + this.mavenDependencies = mavenDependencies; + } + + public List getMavenManagedDependencies() { + return mavenManagedDependencies; + } + + public void setMavenManagedDependencies(List mavenManagedDependencies) { + this.mavenManagedDependencies = mavenManagedDependencies; + } + + public List getMavenPlugins() { + return mavenPlugins; + } + + public void setMavenPlugins(List mavenPlugins) { + this.mavenPlugins = mavenPlugins; + } + + public boolean isSupplier() { + return type == AppType.source; + } + + public boolean isConsumer() { + return type == AppType.sink; + } + + public boolean isFunction() { + return type == AppType.processor; + } + + public String getConfigClass() { + return configClass; + } + + public void setConfigClass(String configClass) { + this.configClass = configClass; + } + + public List getMetadataSourceTypeFilters() { + return metadataSourceTypeFilters; + } + + public void setMetadataSourceTypeFilters(List metadataSourceTypeFilters) { + this.metadataSourceTypeFilters = metadataSourceTypeFilters; + } + + public List getMetadataNameFilters() { + return metadataNameFilters; + } + + public void setMetadataNameFilters(List metadataNameFilters) { + this.metadataNameFilters = metadataNameFilters; + } + + public ContainerImageFormat getContainerImageFormat() { + return containerImageFormat; + } + + public void setContainerImageFormat(ContainerImageFormat containerImageFormat) { + this.containerImageFormat = containerImageFormat; + } + + public boolean isEnableContainerImageMetadata() { + return enableContainerImageMetadata; + } + + public void setEnableContainerImageMetadata(boolean enableContainerImageMetadata) { + this.enableContainerImageMetadata = enableContainerImageMetadata; + } + + public String getContainerImageOrgName() { + return containerImageOrgName; + } + + public void setContainerImageOrgName(String containerImageOrgName) { + this.containerImageOrgName = containerImageOrgName; + } + + public String getContainerImageTag() { + return containerImageTag; + } + + public void setContainerImageTag(String containerImageTag) { + this.containerImageTag = containerImageTag; + } + + public String getFunctionDefinition() { + return functionDefinition; + } + + public void setFunctionDefinition(String functionDefinition) { + this.functionDefinition = functionDefinition; + } + + public enum ContainerImageFormat {Docker, OCI} + + public enum AppType {source, processor, sink} +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java new file mode 100644 index 00000000..168bdd07 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -0,0 +1,221 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +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 com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; +import org.apache.commons.io.FileUtils; + +import org.springframework.util.Assert; + +/** + * @author Christian Tzolov + */ +public 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 containerTemplateProperties = new HashMap<>(); + // register {{#capitalize}}...{{/capitalize}} function. + containerTemplateProperties.put("capitalize", (Mustache.Lambda) (frag, out) -> out.write(capitalize(frag.execute().trim()))); + // register {{#camelCase}}...{{/camelCase}} function. + containerTemplateProperties.put("camelCase", (Mustache.Lambda) (frag, out) -> out.write(camelCase(frag.execute().trim()))); + + containerTemplateProperties.put("bom", generatorProperties.getAppBom()); + containerTemplateProperties.put("app", generatorProperties.getAppDefinition()); + containerTemplateProperties.put("binders", generatorProperties.getBinders()); + + // --------------------------------- + // Generate apps container POM + // --------------------------------- + File appParentDir = mkdirs(generatorProperties.getOutputFolder()); + copy(materialize("template/apps-container-pom.xml", containerTemplateProperties), + file(appParentDir, "pom.xml")); + // maven wrapper + copyMavenWrapper(appParentDir); + + // --------------------------------- + // Generate App projects + // --------------------------------- + Assert.notEmpty(generatorProperties.getBinders(), "At least one Binder must be provided"); + for (String binder : generatorProperties.getBinders()) { + generateAppProject(appParentDir, containerTemplateProperties, generatorProperties.getAppDefinition(), + generatorProperties.getProjectResourcesDirectory(), binder); + } + } + + private void generateAppProject(File appRootDirectory, Map containerTemplateProperties, + AppDefinition appDefinition, File projectResourcesDirectory, String binder) throws IOException { + + String appClassName = String.format("%s%s%sApplication", + camelCase(appDefinition.getName()), + capitalize(appDefinition.getType().name()), + capitalize(binder)); + + String appPackageName = String.format("org.springframework.cloud.stream.app.%s.%s.%s", + toPkg(appDefinition.getName()), + appDefinition.getType(), + binder); + + Map 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)); + + 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")); + + // application.properties + copy(materialize("template/app.properties", appTemplateProperties), + file(appMainResourceDir, "application.properties")); + + // copy the entire project's src/main/resources directory + if (projectResourcesDirectory != null && projectResourcesDirectory.exists()) { + FileUtils.copyDirectory(projectResourcesDirectory, appMainResourceDir); + } + + 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 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); + } + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java new file mode 100644 index 00000000..b5d8cd32 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java @@ -0,0 +1,91 @@ +/* + * 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 { + + /** + * Common pom versions + */ + private AppBom appBom; + + /** + * 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 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 AppBom getAppBom() { + return appBom; + } + + public void setAppBom(AppBom appBom) { + this.appBom = appBom; + } + + public AppDefinition getAppDefinition() { + return appDefinition; + } + + public void setAppDefinition(AppDefinition appDefinition) { + this.appDefinition = appDefinition; + } + + public List getBinders() { + return binders; + } + + public void setBinders(List binders) { + this.binders = binders; + } + + public File getProjectResourcesDirectory() { + return projectResourcesDirectory; + } + + public void setProjectResourcesDirectory(File projectResourcesDirectory) { + this.projectResourcesDirectory = projectResourcesDirectory; + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/jvm.config b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/jvm.config new file mode 100644 index 00000000..0e7dabef --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/jvm.config @@ -0,0 +1 @@ +-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom \ No newline at end of file diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/maven.config b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/maven.config new file mode 100644 index 00000000..3b8cf46e --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/maven.config @@ -0,0 +1 @@ +-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/MavenWrapperDownloader.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100755 index 00000000..2e394d5b --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/MavenWrapperDownloader.java @@ -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(); + } + +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.jar b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.jar new file mode 100755 index 0000000000000000000000000000000000000000..01e67997377a393fd672c7dcde9dccbedf0cb1e9 GIT binary patch literal 48337 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 + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + {{bom.springBootVersion}} + + + org.springframework.cloud.stream.app + {{app.name}}-{{app.type}}-{{app-binder}} + {{app.version}} + {{app.name}}-{{app.type}}-{{app-binder}} + Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder}}{{/capitalize}} Binder Application + + + + + repo.spring.io + Spring Release Repository + https://repo.spring.io/libs-release-local + + + repo.spring.io + Spring Snapshot Repository + https://repo.spring.io/libs-snapshot-local + + + + + true + UTF-8 + + + + + + {{#app.mavenManagedDependencies}} + {{this}} + {{/app.mavenManagedDependencies}} + + + + + + + {{#app.mavenDependencies}} + {{this}} + {{/app.mavenDependencies}} + + + org.springframework.cloud + spring-cloud-stream-binder-{{app-binder}} + + + org.springframework.cloud.stream.app + stream-applications-postprocessor-common + ${project.version} + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + {{#app.mavenPlugins}} + {{this}} + {{/app.mavenPlugins}} + + + org.springframework.boot + spring-boot-maven-plugin + + + + com.google.cloud.tools + jib-maven-plugin + 2.0.0 + + + springcloud/openjdk + + + {{app.containerImageOrgName}}/${project.artifactId} + + {{app.containerImageTag}} + + + + USE_CURRENT_TIMESTAMP + {{app.containerImageFormat}} + {{#app.enableContainerImageMetadata}} + + + ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} + + + {{/app.enableContainerImageMetadata}} + + + + + {{#app.enableContainerImageMetadata}} + + + org.codehaus.mojo + properties-maven-plugin + 1.0.0 + + + process-classes + + read-project-properties + + + + + ${project.build.outputDirectory}/META-INF/spring-configuration-metadata-encoded.properties + + + + + + + {{/app.enableContainerImageMetadata}} + + + org.springframework.cloud + spring-cloud-app-starter-metadata-maven-plugin + {{bom.appMetadataMavenPluginVersion}} + + {{#app.enableContainerImageMetadata}} + true + + + {{#app.metadataNameFilters}} + {{this}} + {{/app.metadataNameFilters}} + + + {{#app.metadataSourceTypeFilters}} + {{this}} + {{/app.metadataSourceTypeFilters}} + + + {{/app.enableContainerImageMetadata}} + + + + aggregate-metadata + compile + + aggregate-metadata + + + + + + maven-surefire-plugin + 2.21.0 + + ${skipTests} + + + + maven-javadoc-plugin + + + javadoc + package + + jar + + + + + true + + + + maven-source-plugin + + + attach-sources + package + + jar + + + + + + + + + milestone + + + repo.spring.io + Spring Milestone Repository + https://repo.spring.io/libs-milestone-local + + + + + central + + + + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties new file mode 100644 index 00000000..e50e7ad0 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties @@ -0,0 +1,26 @@ +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 + +spring.cloud.stream.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.additionalProperties}} +{{this}} +{{/app.additionalProperties}} + + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-container-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-container-pom.xml new file mode 100644 index 00000000..ed81bf23 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-container-pom.xml @@ -0,0 +1,103 @@ + + + + 4.0.0 + org.springframework.cloud.stream.app + {{app.name}}-{{app.type}}-apps + {{app.version}} + pom + + Apps Container + Container project for generated apps + http://spring.io/spring-cloud + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + 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. + + + + + schacko + Soby Chacko + schacko at pivotal.io + Pivotal Software, Inc. + http://www.spring.io + + developer + + + + tzolov + Christian Tzolov + ctzolov at pivotal.io + Pivotal Software, Inc. + http://www.spring.io + + developer + + + + + + {{#binders}} + {{app.name}}-{{app.type}}-{{this}} + {{/binders}} + + + + scm:git:git://github.com/spring-cloud/spring-cloud-stream-app-starters.git + scm:git:ssh://git@github.com/spring-cloud/spring-cloud-stream-app-starters.git + https://github.com/spring-cloud/spring-cloud-stream-app-starters + + + + repo.spring.io + Spring Release Repository + https://repo.spring.io/libs-release-local + + + repo.spring.io + Spring Snapshot Repository + https://repo.spring.io/libs-snapshot-local + + + + + + com.google.cloud.tools + jib-maven-plugin + 2.0.0 + + + + + + milestone + + + repo.spring.io + Spring Milestone Repository + https://repo.spring.io/libs-milestone-local + + + + + + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw new file mode 100755 index 00000000..a08b219e --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw @@ -0,0 +1,253 @@ +#!/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 '\!//' -e 's!.*$!!' ) +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 + echo Deactivating \"milestone\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') +fi + +if echo $VERSION | egrep -q 'RELEASE'; then + echo Activating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" +else + echo Deactivating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') +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} "$@" diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw.cmd b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw.cmd new file mode 100644 index 00000000..eb9a292a --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw.cmd @@ -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% diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/templates/assembly.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/templates/assembly.xml new file mode 100644 index 00000000..690a8327 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/templates/assembly.xml @@ -0,0 +1,15 @@ + + ${artifactId} + + + + ${groupId}:${artifactId} + + . + ${artifactId}.jar + + + \ No newline at end of file diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java new file mode 100644 index 00000000..7f11025c --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -0,0 +1,115 @@ +/* + * 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 + * + * 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 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.List; + +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(); + + Model pomModel = getModel(new File("./target/apps")); + + List 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"); + 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 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}" + + ""); + assertThat(jibPlugin.getConfiguration().toString()).contains("testspringcloud/${project.artifactId}"); + assertThat(jibPlugin.getConfiguration().toString()).contains("3.0.0.BUILD-SNAPSHOT"); + + assertThat(pomModel.getRepositories().size()).isEqualTo(2); + } + + 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); + } + } +} + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java new file mode 100644 index 00000000..586fa92e --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -0,0 +1,191 @@ +/* + * 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 + * + * 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 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.Arrays; +import java.util.List; + +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.pull.XmlPullParserException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import org.springframework.cloud.stream.app.plugin.generator.AppDefinition; +import org.springframework.util.ReflectionUtils; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Christian Tzolov + */ +public class SpringCloudStreamAppGeneratorMojoTest { + + @Rule + public TemporaryFolder projectHome = new TemporaryFolder(); + + private SpringCloudStreamAppGeneratorMojo springCloudStreamAppMojo = new SpringCloudStreamAppGeneratorMojo(); + + private Class mojoClazz = springCloudStreamAppMojo.getClass(); + + @Before + public void before() throws NoSuchFieldException { + + SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); + containerImage.setFormat(AppDefinition.ContainerImageFormat.Docker); + + setMojoProperty("containerImage", containerImage); + + SpringCloudStreamAppGeneratorMojo.GeneratedApp generatedApp = new SpringCloudStreamAppGeneratorMojo.GeneratedApp(); + generatedApp.setName("log"); + generatedApp.setType(AppDefinition.AppType.sink); + generatedApp.setVersion("3.0.0.BUILD-SNAPSHOT"); + generatedApp.setConfigClass("io.pivotal.java.function.log.consumer.LogConsumerConfiguration.class"); + + setMojoProperty("generatedApp", generatedApp); + + setMojoProperty("metadataSourceTypeFilters", Arrays.asList("io.pivotal.java.function.log.consumer.LogConsumerProperties")); + setMojoProperty("metadataNameFilters", Arrays.asList("server.port")); + + setMojoProperty("additionalAppProperties", Arrays.asList( + "spring.cloud.streamapp.security.enabled=false", + "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"); + setMojoProperty("dependencies", Arrays.asList(dep)); + + + setMojoProperty("binders", Arrays.asList("kafka", "rabbit")); + + // BOM + setMojoProperty("bootVersion", "2.3.0.M1"); + setMojoProperty("appMetadataMavenPluginVersion", "1.0.2.BUILD-SNAPSHOT"); + + //setMojoProperty("generatedProjectHome", "./target/apps"); + setMojoProperty("generatedProjectHome", projectHome.getRoot().getAbsolutePath()); + } + + @Test + public void testWithDisabledContainerMetadata() throws Exception { + + // disable metadata in container image (Default) + SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); + containerImage.setEnableMetadata(false); + setMojoProperty("containerImage", containerImage); + + this.springCloudStreamAppMojo.execute(); + + //Model pomModel = getModel(new File("./target/apps")); + Model pomModel = getModel(new File(projectHome.getRoot().getAbsolutePath())); + List 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}" + + ""); + } + + @Test + public void testDefaultProjectCreationByPlugin() throws Exception { + + // Enable Metadata in Container Image! + SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); + containerImage.setEnableMetadata(true); + setMojoProperty("containerImage", containerImage); + + this.springCloudStreamAppMojo.execute(); + + //assertGeneratedPomXml(new File("./target/apps")); + assertGeneratedPomXml(new File(projectHome.getRoot().getAbsolutePath())); + } + + private void assertGeneratedPomXml(File rootPath) throws Exception { + + Model pomModel = getModel(rootPath); + + List dependencies = pomModel.getDependencies(); + assertThat(dependencies.size()).isEqualTo(4); + + 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); + + assertThat(dependencies.stream() + .filter(d -> d.getArtifactId().equals("stream-applications-postprocessor-common")).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 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}" + + ""); + + 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 = this.mojoClazz.getDeclaredField(propertyName); + mojoProperty.setAccessible(true); + ReflectionUtils.setField(mojoProperty, this.springCloudStreamAppMojo, value); + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml new file mode 100644 index 00000000..05196756 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -0,0 +1,226 @@ + + + 4.0.0 + + org.springframework.cloud.stream.app + http-source-apps + 3.0.0.BUILD-SNAPSHOT + http-source-apps + http source apps + jar + + + 2.3.0.M1 + 3.0.0.BUILD-SNAPSHOT + 2.1.1.RELEASE + 1.0.2.BUILD-SNAPSHOT + + + 1.0.0.BUILD-SNAPSHOT + Hoxton.RELEASE + Horsham.SR2 + 3.0.2.RELEASE + + + + + + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + + app-gen + package + + generate-app + + + + + + + ${spring-boot.version} + + + kafka + rabbit + + + + org.springframework.cloud + spring-cloud-stream-dependencies + ${spring-cloud-stream-dependencies.version} + + + org.springframework.cloud + spring-cloud-function-dependencies + ${spring-cloud-function-dependencies.version} + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + + + + + + org.springframework.cloud.stream.app + stream-apps-security-common + ${stream-apps-core.version} + + + org.springframework.cloud.stream.app + stream-apps-micrometer-common + ${stream-apps-core.version} + + + io.micrometer + micrometer-registry-influx + + + io.micrometer + micrometer-registry-prometheus + + + io.micrometer + micrometer-registry-datadog + + + io.pivotal.cfenv + java-cfenv-boot + ${java-cfenv-boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-security + + + + + org.logaritex + test-additional-maven-plugin + 3.0.0 + + + springcloud/openjdk + + + springcloudstream:${project.artifactId} + + latest + + + + USE_CURRENT_TIMESTAMP + Docker + + ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} + + + ]]> + + + + sign-artifacts + verify + + sign + + + + + + + + + + + + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + + ./target/apps + + + testspringcloud + true + + + + http + source + ${project.version} + io.pivotal.java.function.http.supplier.HttpSupplierConfiguration.class + + + + + io.pivotal.java.function + http-supplier + ${java-functions.version} + + + + + spring.main.web-application-type=reactive + spring.cloud.streamapp.security.enabled=false + spring.cloud.streamapp.security.csrf-enabled=false + + + + io.pivotal.java.function.http.supplier.HttpSourceProperties + io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors + + + + server.port + + + + + + org.springframework.cloud + spring-cloud-app-starter-metadata-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + true + + + + server.port + + + io.pivotal.java.function.http.supplier.HttpSourceProperties + io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors + + + + + + + + diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/test.txt b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/test.txt new file mode 100644 index 00000000..9daeafb9 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/test.txt @@ -0,0 +1 @@ +test From f21bc1cfcd30e21d41d51a25369a364cd112c64b Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 19 Aug 2020 17:12:54 -0400 Subject: [PATCH 03/76] Fix checkstyle issues --- .../LICENSE => LICENSE | 0 .../mvnw | 253 ------------------ .../mvnw.cmd | 145 ---------- .../app/plugin/generator/AppDefinition.java | 28 +- .../plugin/generator/ProjectGenerator.java | 4 +- .../generator/ProjectGeneratorProperties.java | 4 +- .../.mvn/wrapper/maven-wrapper.properties | 2 +- .../dataflow/app/plugin/MojoHarnessTest.java | 3 +- ...SpringCloudStreamAppGeneratorMojoTest.java | 4 +- 9 files changed, 33 insertions(+), 410 deletions(-) rename spring-cloud-dataflow-apps-generator-plugin/LICENSE => LICENSE (100%) delete mode 100755 spring-cloud-dataflow-apps-generator-plugin/mvnw delete mode 100644 spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd diff --git a/spring-cloud-dataflow-apps-generator-plugin/LICENSE b/LICENSE similarity index 100% rename from spring-cloud-dataflow-apps-generator-plugin/LICENSE rename to LICENSE diff --git a/spring-cloud-dataflow-apps-generator-plugin/mvnw b/spring-cloud-dataflow-apps-generator-plugin/mvnw deleted file mode 100755 index a08b219e..00000000 --- a/spring-cloud-dataflow-apps-generator-plugin/mvnw +++ /dev/null @@ -1,253 +0,0 @@ -#!/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 '\!//' -e 's!.*$!!' ) -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 - echo Deactivating \"milestone\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') -fi - -if echo $VERSION | egrep -q 'RELEASE'; then - echo Activating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" -else - echo Deactivating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') -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} "$@" diff --git a/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd b/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd deleted file mode 100644 index eb9a292a..00000000 --- a/spring-cloud-dataflow-apps-generator-plugin/mvnw.cmd +++ /dev/null @@ -1,145 +0,0 @@ -@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% diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 70c67102..5f03d2ac 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -36,7 +36,7 @@ public class AppDefinition { private List mavenDependencies = new ArrayList<>(); private List mavenPlugins = new ArrayList<>(); /** - * Allow to generate either Docker or OCI image formats + * Allow to generate either Docker or OCI image formats. */ private ContainerImageFormat containerImageFormat = ContainerImageFormat.Docker; /** @@ -178,7 +178,29 @@ public class AppDefinition { this.functionDefinition = functionDefinition; } - public enum ContainerImageFormat {Docker, OCI} + public enum ContainerImageFormat { + /** + * Docker image format. + */ + Docker, + /** + * OCI format. + */ + OCI + } - public enum AppType {source, processor, sink} + public enum AppType { + /** + * source type. + */ + source, + /** + * processor type. + */ + processor, + /** + * sink type. + */ + sink + } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java index 168bdd07..54ac1a8e 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -37,7 +37,7 @@ import org.springframework.util.Assert; /** * @author Christian Tzolov */ -public class ProjectGenerator { +public final class ProjectGenerator { private ProjectGenerator() { } @@ -214,7 +214,7 @@ public class ProjectGenerator { private void copyResource(String resourcePath, File toFile) throws IOException { try (InputStream resourcesStream = - Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(resourcePath))) { + Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(resourcePath))) { Files.copy(resourcesStream, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java index b5d8cd32..ed54029a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java @@ -25,12 +25,12 @@ import java.util.List; public class ProjectGeneratorProperties { /** - * Common pom versions + * Common pom versions. */ private AppBom appBom; /** - * Source, processor and sink application configuration + * Source, processor and sink application configuration. */ private AppDefinition appDefinition; diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.properties b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.properties index 00d32aab..71793467 100755 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.properties +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index 7f11025c..1df9c626 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -35,7 +35,6 @@ import org.junit.rules.TemporaryFolder; import static org.assertj.core.api.Assertions.assertThat; - /** * @author Christian Tzolov */ diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index 586fa92e..8eb582e8 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -35,7 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.springframework.cloud.stream.app.plugin.generator.AppDefinition; +import org.springframework.cloud.dataflow.app.plugin.generator.AppDefinition; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; From 5676be8241fd79f4c882aad1c145f255ab9e2e8b Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 20 Aug 2020 10:22:20 +0200 Subject: [PATCH 04/76] Fix test fixtures to resolve test failures. --- .../README.adoc | 115 +++++++++--------- .../dataflow/app/plugin/MavenXmlWriter.java | 60 ++++++--- .../resources/unit/http-source-apps/pom.xml | 12 +- 3 files changed, 102 insertions(+), 85 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index a5049b88..fa199102 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -1,5 +1,6 @@ -= spring-cloud-stream-app-maven-plugin -Maven plugin for generating spring cloud stream applications from the spring-cloud-stream-app-starters repository += spring-cloud-dataflow-apps-generator-plugin + +Maven plugin for generating spring cloud stream applications: https://github.com/spring-cloud/stream-applications == Build @@ -10,9 +11,9 @@ mvn clean package [Requires JDK 8] [source, xml] ---- - org.springframework.cloud.stream.app.plugin - spring-cloud-stream-app-maven-plugin - 2.0.1.BUILD-SNAPSHOT + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + 1.0.0-SNAPSHOT 1.8 ${spring-boot.version} @@ -84,19 +85,16 @@ mvn clean package [Requires JDK 8] ---- -The bom for stream apps is specified in spring-cloud-stream-app-dependencies -(group id: org.springframework.cloud.stream.app) +The `bom` for stream apps is specified in `spring-cloud-stream-app-dependencies` (group id: org.springframework.cloud.stream.app) +and for tasks it is spring-cloud-task-app-dependencies (group id: org.springframework.cloud.task.app). -and for tasks it is spring-cloud-task-app-dependencies -(group id: org.springframework.cloud.task.app). - -Extra dependency management can be added as additional boms to the plugin using the property --DbomsWithHigherPrecedence and value based on the pattern groupId:artfiactId:version. This can +Extra dependency management can be added as additional `boms` to the plugin using the property +`-DbomsWithHigherPrecedence` and value based on the pattern `groupId:artfiactId:version`. This can be a comma separated collection of boms. Any bom added like this will get priority in the order they are specified over any default values used in the plugin configuration. - Spring Boot version used in the generated app can be overridden using the plugin property - -DbootVersion=. +Spring Boot version used in the generated app can be overridden using the plugin property + `-DbootVersion=`. ==== Copy Third-Party Resources @@ -105,58 +103,57 @@ generated App Starter classpath. Form example if you want to copy a truststore and a self-signed certificate (`clientKeyStore.jks`, `cacerts`) bundled inside the `org.springframework.cloud.stream.app:coap-app-starters-common:2.0.0.BUILD-SNAPSHOT` dependency you can add this to the -`spring-cloud-stream-app-maven-plugin` configuration: +`spring-cloud-dataflow-apps-generator-plugin` configuration: [source, xml] ---- - - org.springframework.cloud.stream.app.plugin - spring-cloud-stream-app-maven-plugin - - ${session.executionRootDirectory}/apps - ${project.version} - .... - - - - - - org.springframework.cloud.stream.app - coap-app-starters-common - 2.0.0.BUILD-SNAPSHOT - clientKeyStore.jks,cacerts - - - - - + + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + + ${session.executionRootDirectory}/apps + ${project.version} + .... + + + + + + org.springframework.cloud.stream.app + coap-app-starters-common + 2.0.0.BUILD-SNAPSHOT + clientKeyStore.jks,cacerts + + + + ---- Later will add the following `maven-dependency-plugin` definition to the generated aps starter pom: [source, xml] ---- - - maven-dependency-plugin - - - - unpack - - - - - org.springframework.cloud.stream.app - coap-app-starters-common - 2.0.0.BUILD-SNAPSHOT - clientKeyStore.jks,cacerts - - - ${project.build.directory}/classes/ - - - - + + maven-dependency-plugin + + + + unpack + + + + + org.springframework.cloud.stream.app + coap-app-starters-common + 2.0.0.BUILD-SNAPSHOT + clientKeyStore.jks,cacerts + + + ${project.build.directory}/classes/ + + + + ---- This will ensure that the clientKeyStore.jks,cacerts files will be copied to the AppStarter's `BOOT-INF/classes/` @@ -166,8 +163,8 @@ The `additionalAppProperties` configuration block permits adding of additional p [source, xml] ---- - org.springframework.cloud.stream.app.plugin - spring-cloud-stream-app-maven-plugin + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin ${session.executionRootDirectory}/apps ${project.version} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java index 8385dc83..3eab70b3 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java @@ -49,6 +49,10 @@ public final class MavenXmlWriter { *

* 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 element type. + * @return XML text serialization of the element. */ public static String toXml(T element) { String privateMethodName = "write" + element.getClass().getSimpleName(); @@ -62,27 +66,30 @@ public final class MavenXmlWriter { * requires special handling to convert String values into Xpp3Dom object before writing. *

* For Plugin Configuration you must nest the configuration content into a CDATA block like shown here: - * + *

{@code
 	 * 
-	 * 
-	 * com.google.cloud.tools
-	 * jib-maven-plugin
-	 * 2.0.0
-	 * 
-	 * springcloud/openjdk
-	 * 
-	 * springcloudstream:${project.artifactId}
-	 * latest
-	 * 
-	 * 
-	 * Docker
-	 * 
-	 * ]]>
-	 * 
-	 * 
+	 *   
+	 *     com.google.cloud.tools
+	 *     jib-maven-plugin
+	 *     2.0.0
+	 *     
+	 *       springcloud/openjdk
+	 *         
+	 *           springcloudstream:${project.artifactId}
+	 *           latest
+	 *         
+	 *         
+	 *           Docker
+	 *         
+	 *       ]]>
+	 *     
+	 *   
 	 * 
-	 * 
+	 * }
+ * + * @param plugin Plugin instance to serialize. + * @return POM/XML serialization of the plugin. */ public static String toXml(Plugin plugin) { try { @@ -100,6 +107,13 @@ public final class MavenXmlWriter { 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 elementWriter) { try { Writer writer = new StringWriter(); @@ -131,7 +145,7 @@ public final class MavenXmlWriter { * @param serializer The XmlSerializer used to write the xml. */ public static void invokeMavenXppWriteMethod(T modelElementToWrite, String writeMethodName, - String xmlTagName, XmlSerializer serializer) { + String xmlTagName, XmlSerializer serializer) { try { MavenXpp3Writer pomWriter = new MavenXpp3Writer(); @@ -145,6 +159,12 @@ public final class MavenXmlWriter { } } + /** + * 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); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 05196756..8d88a066 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -27,9 +27,9 @@ - org.springframework.cloud.stream.app.plugin - spring-cloud-stream-app-maven-plugin - 2.0.0.BUILD-SNAPSHOT + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + 1.0.0-SNAPSHOT app-gen @@ -158,9 +158,9 @@ - org.springframework.cloud.stream.app.plugin - spring-cloud-stream-app-maven-plugin - 2.0.0.BUILD-SNAPSHOT + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + 1.0.0-SNAPSHOT ./target/apps From 2f361e19c9a710f11a43379312d9cbcdea22f350 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 20 Aug 2020 13:25:11 +0200 Subject: [PATCH 05/76] Allow per Binder configurations. - The new construct allows setting application properties, maven (properties, dependencies, managed dependencies, plugins) per Binder definition. Resolves https://github.com/spring-cloud/spring-cloud-app-starters-maven-plugins/pull/64 --- .../SpringCloudStreamAppGeneratorMojo.java | 146 +++++++++-- .../dataflow/app/plugin/generator/AppBom.java | 57 ----- .../app/plugin/generator/AppDefinition.java | 237 ++++++++++++------ .../plugin/generator/BinderDefinition.java | 95 +++++++ .../plugin/generator/ProjectGenerator.java | 29 ++- .../generator/ProjectGeneratorProperties.java | 19 +- .../src/main/resources/template/README.adoc | 2 +- .../src/main/resources/template/app-pom.xml | 65 +++-- .../main/resources/template/app.properties | 7 +- ...container-pom.xml => apps-modules-pom.xml} | 6 +- ...SpringCloudStreamAppGeneratorMojoTest.java | 15 +- .../resources/unit/http-source-apps/pom.xml | 24 +- 12 files changed, 476 insertions(+), 226 deletions(-) delete mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java rename spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/{apps-container-pom.xml => apps-modules-pom.xml} (95%) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 21c434d7..e3e0c6a0 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -36,8 +36,8 @@ 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.AppBom; 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; @@ -92,10 +92,10 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { List additionalPlugins = new ArrayList<>(); @Parameter - List binders = new ArrayList<>(); + List binders = new ArrayList<>(); // Versions - @Parameter(defaultValue = "2.2.4.RELEASE", required = true) + @Parameter(defaultValue = "2.3.3", required = true) private String bootVersion; @Parameter(defaultValue = "${app-metadata-maven-plugin-version}") @@ -103,12 +103,9 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { @Override public void execute() throws MojoFailureException { - // Bom - AppBom appBom = new AppBom() - .withSpringBootVersion(this.bootVersion) - .withAppMetadataMavenPluginVersion(this.appMetadataMavenPluginVersion); AppDefinition app = new AppDefinition(); + app.setSpringBootVersion(this.bootVersion); app.setName(this.generatedApp.getName()); app.setType(this.generatedApp.getType()); app.setVersion(this.generatedApp.getVersion()); @@ -117,18 +114,20 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { this.populateVisiblePropertiesFromFile(this.metadataSourceTypeFilters, this.metadataNameFilters); + app.getMetadata().setMavenPluginVersion(this.appMetadataMavenPluginVersion); + if (!CollectionUtils.isEmpty(this.metadataSourceTypeFilters)) { - app.setMetadataSourceTypeFilters(this.metadataSourceTypeFilters); + app.getMetadata().setSourceTypeFilters(this.metadataSourceTypeFilters); } if (!CollectionUtils.isEmpty(this.metadataNameFilters)) { - app.setMetadataNameFilters(this.metadataNameFilters); + app.getMetadata().setNameFilters(this.metadataNameFilters); } - app.setAdditionalProperties(this.additionalAppProperties); + app.setProperties(this.additionalAppProperties); // BOM - app.setMavenManagedDependencies(this.boms.stream() + app.getMaven().setManagedDependencies(this.boms.stream() .filter(Objects::nonNull) .map(dependency -> { dependency.setScope("import"); @@ -142,31 +141,55 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { // Dependencies List allDependenciesMerged = new ArrayList<>(this.dependencies); allDependenciesMerged.addAll(this.globalDependencies); - app.setMavenDependencies(allDependenciesMerged.stream() + app.getMaven().setDependencies(allDependenciesMerged.stream() .map(MavenXmlWriter::toXml) .map(xml -> MavenXmlWriter.indent(xml, 8)) .collect(Collectors.toList())); // Plugins - app.setMavenPlugins(this.additionalPlugins.stream() + app.getMaven().setPlugins(this.additionalPlugins.stream() .map(MavenXmlWriter::toXml) .map(d -> MavenXmlWriter.indent(d, 12)) .collect(Collectors.toList())); // Container Image configuration - app.setContainerImageFormat(this.containerImage.getFormat()); - app.setEnableContainerImageMetadata(this.containerImage.isEnableMetadata()); + app.getContainerImage().setFormat(this.containerImage.getFormat()); + app.getContainerImage().setEnableMetadata(this.containerImage.isEnableMetadata()); if (StringUtils.hasText(this.containerImage.getOrgName())) { - app.setContainerImageOrgName(this.containerImage.getOrgName()); + app.getContainerImage().setOrgName(this.containerImage.getOrgName()); } - app.setContainerImageTag(this.generatedApp.getVersion()); + app.getContainerImage().setTag(this.generatedApp.getVersion()); // Generator Properties ProjectGeneratorProperties generatorProperties = new ProjectGeneratorProperties(); - generatorProperties.setBinders(this.binders); + + generatorProperties.setBinders(this.binders.stream() + .map(mavenBinder -> { + BinderDefinition bd = new BinderDefinition(); + bd.setName(mavenBinder.getName()); + bd.setProperties(mavenBinder.getProperties()); + bd.getMaven().setProperties(mavenBinder.getMaven().getProperties()); + bd.getMaven().setDependencies( + mavenBinder.getMaven().getDependencies().stream() + .map(MavenXmlWriter::toXml) + .map(xml -> MavenXmlWriter.indent(xml, 8)) + .collect(Collectors.toList())); + bd.getMaven().setManagedDependencies( + mavenBinder.getMaven().getManagedDependencies().stream() + .map(MavenXmlWriter::toXml) + .map(xml -> MavenXmlWriter.indent(xml, 8)) + .collect(Collectors.toList())); + bd.getMaven().setPlugins( + mavenBinder.getMaven().getPlugins().stream() + .map(MavenXmlWriter::toXml) + .map(d -> MavenXmlWriter.indent(d, 12)) + .collect(Collectors.toList())); + return bd; + }) + .collect(Collectors.toList())); + generatorProperties.setOutputFolder(new File(this.generatedProjectHome)); - generatorProperties.setAppBom(appBom); generatorProperties.setAppDefinition(app); generatorProperties.setProjectResourcesDirectory(this.projectResourcesDir); @@ -286,12 +309,12 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { private String functionType() { switch (this.type) { - case processor: - return "Function"; - case sink: - return "Consumer"; - case source: - return "Supplier"; + case processor: + return "Function"; + case sink: + return "Consumer"; + case source: + return "Supplier"; } throw new IllegalArgumentException("Unknown App type:" + this.type); } @@ -326,4 +349,77 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { this.enableMetadata = enableMetadata; } } + + public static class Binder { + + private String name; + + private final Maven maven = new Maven(); + + private List properties = new ArrayList<>(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Maven getMaven() { + return maven; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public static class Maven { + + private List properties = new ArrayList<>(); + + private List managedDependencies = new ArrayList<>(); + + private List dependencies = new ArrayList<>(); + + private List plugins = new ArrayList<>(); + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public List getManagedDependencies() { + return managedDependencies; + } + + public void setManagedDependencies(List managedDependencies) { + this.managedDependencies = managedDependencies; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public List getPlugins() { + return plugins; + } + + public void setPlugins(List plugins) { + this.plugins = plugins; + } + } + } + } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java deleted file mode 100644 index f5d55bf3..00000000 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppBom.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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; - -/** - * @author Christian Tzolov - */ -public class AppBom { - /** - * Spring Boot version to use. - */ - private String springBootVersion; - - private String appMetadataMavenPluginVersion; - - - public String getSpringBootVersion() { - return springBootVersion; - } - - public void setSpringBootVersion(String springBootVersion) { - this.springBootVersion = springBootVersion; - } - - public AppBom withSpringBootVersion(String springBootVersion) { - this.springBootVersion = springBootVersion; - return this; - } - - public String getAppMetadataMavenPluginVersion() { - return appMetadataMavenPluginVersion; - } - - public void setAppMetadataMavenPluginVersion(String appMetadataMavenPluginVersion) { - this.appMetadataMavenPluginVersion = appMetadataMavenPluginVersion; - } - - public AppBom withAppMetadataMavenPluginVersion(String appMetadataMavenPluginVersion) { - this.appMetadataMavenPluginVersion = appMetadataMavenPluginVersion; - return this; - } - -} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 5f03d2ac..aae4964f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -24,27 +24,55 @@ import java.util.List; */ public class AppDefinition { + /** + * 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; + + /** + * Spring Cloud Function defintion. + */ private String functionDefinition; - private List metadataSourceTypeFilters = new ArrayList<>(); - private List metadataNameFilters = new ArrayList<>(); - private List additionalProperties = new ArrayList<>(); - private List mavenManagedDependencies = new ArrayList<>(); - private List mavenDependencies = new ArrayList<>(); - private List mavenPlugins = new ArrayList<>(); + /** - * Allow to generate either Docker or OCI image formats. + * Additional application properties. */ - private ContainerImageFormat containerImageFormat = ContainerImageFormat.Docker; + private List properties = new ArrayList<>(); + /** - * True will attempt to inline the (white) filtered Spring Boot metadata as Base64 encoded property. + * Maven related configurations to be applied for this App definition. */ - private boolean enableContainerImageMetadata = false; - private String containerImageOrgName = "springcloudstream"; - private String containerImageTag = "latest"; + private final Maven maven = new Maven(); + + /** + * 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(); public String getName() { return name; @@ -70,36 +98,32 @@ public class AppDefinition { this.version = version; } - public List getAdditionalProperties() { - return additionalProperties; + public String getSpringBootVersion() { + return springBootVersion; } - public void setAdditionalProperties(List additionalProperties) { - this.additionalProperties = additionalProperties; + public void setSpringBootVersion(String springBootVersion) { + this.springBootVersion = springBootVersion; } - public List getMavenDependencies() { - return mavenDependencies; + public List getProperties() { + return properties; } - public void setMavenDependencies(List mavenDependencies) { - this.mavenDependencies = mavenDependencies; + public void setProperties(List properties) { + this.properties = properties; } - public List getMavenManagedDependencies() { - return mavenManagedDependencies; + public Maven getMaven() { + return maven; } - public void setMavenManagedDependencies(List mavenManagedDependencies) { - this.mavenManagedDependencies = mavenManagedDependencies; + public ContainerImage getContainerImage() { + return containerImage; } - public List getMavenPlugins() { - return mavenPlugins; - } - - public void setMavenPlugins(List mavenPlugins) { - this.mavenPlugins = mavenPlugins; + public Metadata getMetadata() { + return metadata; } public boolean isSupplier() { @@ -122,54 +146,6 @@ public class AppDefinition { this.configClass = configClass; } - public List getMetadataSourceTypeFilters() { - return metadataSourceTypeFilters; - } - - public void setMetadataSourceTypeFilters(List metadataSourceTypeFilters) { - this.metadataSourceTypeFilters = metadataSourceTypeFilters; - } - - public List getMetadataNameFilters() { - return metadataNameFilters; - } - - public void setMetadataNameFilters(List metadataNameFilters) { - this.metadataNameFilters = metadataNameFilters; - } - - public ContainerImageFormat getContainerImageFormat() { - return containerImageFormat; - } - - public void setContainerImageFormat(ContainerImageFormat containerImageFormat) { - this.containerImageFormat = containerImageFormat; - } - - public boolean isEnableContainerImageMetadata() { - return enableContainerImageMetadata; - } - - public void setEnableContainerImageMetadata(boolean enableContainerImageMetadata) { - this.enableContainerImageMetadata = enableContainerImageMetadata; - } - - public String getContainerImageOrgName() { - return containerImageOrgName; - } - - public void setContainerImageOrgName(String containerImageOrgName) { - this.containerImageOrgName = containerImageOrgName; - } - - public String getContainerImageTag() { - return containerImageTag; - } - - public void setContainerImageTag(String containerImageTag) { - this.containerImageTag = containerImageTag; - } - public String getFunctionDefinition() { return functionDefinition; } @@ -203,4 +179,109 @@ public class AppDefinition { */ sink } + + public static class Maven { + private List managedDependencies = new ArrayList<>(); + private List dependencies = new ArrayList<>(); + private List plugins = new ArrayList<>(); + + public List getManagedDependencies() { + return managedDependencies; + } + + public void setManagedDependencies(List managedDependencies) { + this.managedDependencies = managedDependencies; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public List getPlugins() { + return plugins; + } + + public void setPlugins(List plugins) { + this.plugins = plugins; + } + } + + 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"; + + 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 static class Metadata { + private List sourceTypeFilters = new ArrayList<>(); + private List nameFilters = new ArrayList<>(); + private String mavenPluginVersion; + + public List getSourceTypeFilters() { + return sourceTypeFilters; + } + + public void setSourceTypeFilters(List sourceTypeFilters) { + this.sourceTypeFilters = sourceTypeFilters; + } + + public List getNameFilters() { + return nameFilters; + } + + public void setNameFilters(List nameFilters) { + this.nameFilters = nameFilters; + } + + public String getMavenPluginVersion() { + return mavenPluginVersion; + } + + public void setMavenPluginVersion(String mavenPluginVersion) { + this.mavenPluginVersion = mavenPluginVersion; + } + } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java new file mode 100644 index 00000000..28d3fce1 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java @@ -0,0 +1,95 @@ +/* + * 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; + +/** + * @author Christian Tzolov + */ +public class BinderDefinition { + + private String name; + + private final Maven maven = new Maven(); + + private List properties = new ArrayList<>(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Maven getMaven() { + return maven; + } + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public static class Maven { + + private List properties = new ArrayList<>(); + + private List managedDependencies = new ArrayList<>(); + + private List dependencies = new ArrayList<>(); + + private List plugins = new ArrayList<>(); + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public List getManagedDependencies() { + return managedDependencies; + } + + public void setManagedDependencies(List managedDependencies) { + this.managedDependencies = managedDependencies; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public List getPlugins() { + return plugins; + } + + public void setPlugins(List plugins) { + this.plugins = plugins; + } + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java index 54ac1a8e..872992f9 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -88,21 +88,20 @@ public final class ProjectGenerator { public void generate(ProjectGeneratorProperties generatorProperties) throws IOException { - Map containerTemplateProperties = new HashMap<>(); + Map modulesTemplateProperties = new HashMap<>(); // register {{#capitalize}}...{{/capitalize}} function. - containerTemplateProperties.put("capitalize", (Mustache.Lambda) (frag, out) -> out.write(capitalize(frag.execute().trim()))); + modulesTemplateProperties.put("capitalize", (Mustache.Lambda) (frag, out) -> out.write(capitalize(frag.execute().trim()))); // register {{#camelCase}}...{{/camelCase}} function. - containerTemplateProperties.put("camelCase", (Mustache.Lambda) (frag, out) -> out.write(camelCase(frag.execute().trim()))); + modulesTemplateProperties.put("camelCase", (Mustache.Lambda) (frag, out) -> out.write(camelCase(frag.execute().trim()))); - containerTemplateProperties.put("bom", generatorProperties.getAppBom()); - containerTemplateProperties.put("app", generatorProperties.getAppDefinition()); - containerTemplateProperties.put("binders", generatorProperties.getBinders()); + modulesTemplateProperties.put("app", generatorProperties.getAppDefinition()); + modulesTemplateProperties.put("binders", generatorProperties.getBinders()); // --------------------------------- // Generate apps container POM // --------------------------------- File appParentDir = mkdirs(generatorProperties.getOutputFolder()); - copy(materialize("template/apps-container-pom.xml", containerTemplateProperties), + copy(materialize("template/apps-modules-pom.xml", modulesTemplateProperties), file(appParentDir, "pom.xml")); // maven wrapper copyMavenWrapper(appParentDir); @@ -111,24 +110,24 @@ public final class ProjectGenerator { // Generate App projects // --------------------------------- Assert.notEmpty(generatorProperties.getBinders(), "At least one Binder must be provided"); - for (String binder : generatorProperties.getBinders()) { - generateAppProject(appParentDir, containerTemplateProperties, generatorProperties.getAppDefinition(), + for (BinderDefinition binder : generatorProperties.getBinders()) { + generateAppProject(appParentDir, modulesTemplateProperties, generatorProperties.getAppDefinition(), generatorProperties.getProjectResourcesDirectory(), binder); } } private void generateAppProject(File appRootDirectory, Map containerTemplateProperties, - AppDefinition appDefinition, File projectResourcesDirectory, String binder) throws IOException { + AppDefinition appDefinition, File projectResourcesDirectory, BinderDefinition binder) throws IOException { String appClassName = String.format("%s%s%sApplication", camelCase(appDefinition.getName()), capitalize(appDefinition.getType().name()), - capitalize(binder)); + capitalize(binder.getName())); String appPackageName = String.format("org.springframework.cloud.stream.app.%s.%s.%s", toPkg(appDefinition.getName()), appDefinition.getType(), - binder); + binder.getName()); Map appTemplateProperties = new HashMap<>(containerTemplateProperties); @@ -139,7 +138,7 @@ public final class ProjectGenerator { // app POM File appDir = - mkdirs(file(appRootDirectory, appDefinition.getName() + "-" + appDefinition.getType() + "-" + binder)); + mkdirs(file(appRootDirectory, appDefinition.getName() + "-" + appDefinition.getType() + "-" + binder.getName())); copy(materialize("template/app-pom.xml", appTemplateProperties), file(appDir, "pom.xml")); @@ -213,8 +212,8 @@ public final class ProjectGenerator { } private void copyResource(String resourcePath, File toFile) throws IOException { - try (InputStream resourcesStream = - Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(resourcePath))) { + try (InputStream resourcesStream = Objects.requireNonNull( + this.getClass().getClassLoader().getResourceAsStream(resourcePath))) { Files.copy(resourcesStream, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java index ed54029a..b307ea4b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java @@ -24,11 +24,6 @@ import java.util.List; */ public class ProjectGeneratorProperties { - /** - * Common pom versions. - */ - private AppBom appBom; - /** * Source, processor and sink application configuration. */ @@ -42,7 +37,7 @@ public class ProjectGeneratorProperties { /** * List of binders to generate applications for. */ - private List binders; + private List binders; /** * The location of a project's src/main/resources directory. @@ -57,14 +52,6 @@ public class ProjectGeneratorProperties { this.outputFolder = outputFolder; } - public AppBom getAppBom() { - return appBom; - } - - public void setAppBom(AppBom appBom) { - this.appBom = appBom; - } - public AppDefinition getAppDefinition() { return appDefinition; } @@ -73,11 +60,11 @@ public class ProjectGeneratorProperties { this.appDefinition = appDefinition; } - public List getBinders() { + public List getBinders() { return binders; } - public void setBinders(List binders) { + public void setBinders(List binders) { this.binders = binders; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/README.adoc index 3b058ea9..8bfbe8b6 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/README.adoc @@ -1 +1 @@ -= Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder}}{{/capitalize}} Binder Application += Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder.name}}{{/capitalize}} Binder Application diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 541c2544..9fbffec6 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -6,14 +6,14 @@ org.springframework.boot spring-boot-starter-parent - {{bom.springBootVersion}} + {{app.springBootVersion}} org.springframework.cloud.stream.app - {{app.name}}-{{app.type}}-{{app-binder}} + {{app.name}}-{{app.type}}-{{app-binder.name}} {{app.version}} - {{app.name}}-{{app.type}}-{{app-binder}} - Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder}}{{/capitalize}} Binder Application + {{app.name}}-{{app.type}}-{{app-binder.name}} + Spring Cloud Stream {{#camelCase}}{{app.name}}{{/camelCase}} {{#capitalize}}{{app.type}}{{/capitalize}} {{#capitalize}}{{app-binder.name}}{{/capitalize}} Binder Application @@ -32,28 +32,37 @@ true UTF-8 + + {{#app-binder.maven.properties}} + {{this}} + {{/app-binder.maven.properties}} + - {{#app.mavenManagedDependencies}} + {{#app.maven.managedDependencies}} {{this}} - {{/app.mavenManagedDependencies}} + {{/app.maven.managedDependencies}} + + {{#app-binder.maven.managedDependencies}} + {{this}} + {{/app-binder.maven.managedDependencies}} - {{#app.mavenDependencies}} + {{#app.maven.dependencies}} {{this}} - {{/app.mavenDependencies}} + {{/app.maven.dependencies}} + + {{#app-binder.maven.dependencies}} + {{this}} + {{/app-binder.maven.dependencies}} - - org.springframework.cloud - spring-cloud-stream-binder-{{app-binder}} - org.springframework.cloud.stream.app stream-applications-postprocessor-common @@ -105,9 +114,13 @@ - {{#app.mavenPlugins}} + {{#app.maven.plugins}} {{this}} - {{/app.mavenPlugins}} + {{/app.maven.plugins}} + + {{#app-binder.maven.plugins}} + {{this}} + {{/app-binder.maven.plugins}} org.springframework.boot @@ -123,26 +136,26 @@ springcloud/openjdk - {{app.containerImageOrgName}}/${project.artifactId} + {{app.containerImage.orgName}}/${project.artifactId} - {{app.containerImageTag}} + {{app.containerImage.tag}} USE_CURRENT_TIMESTAMP - {{app.containerImageFormat}} - {{#app.enableContainerImageMetadata}} + {{app.containerImage.format}} + {{#app.containerImage.enableMetadata}} ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} - {{/app.enableContainerImageMetadata}} + {{/app.containerImage.enableMetadata}} - {{#app.enableContainerImageMetadata}} + {{#app.containerImage.enableMetadata}} + {{#app.maven.properties}} + {{this}} + {{/app.maven.properties}} + {{#app-binder.maven.properties}} {{this}} {{/app-binder.maven.properties}} @@ -42,13 +46,13 @@ - {{#app.maven.managedDependencies}} + {{#app.maven.dependencyManagement}} {{this}} - {{/app.maven.managedDependencies}} + {{/app.maven.dependencyManagement}} - {{#app-binder.maven.managedDependencies}} + {{#app-binder.maven.dependencyManagement}} {{this}} - {{/app-binder.maven.managedDependencies}} + {{/app-binder.maven.dependencyManagement}} @@ -130,7 +134,7 @@ com.google.cloud.tools jib-maven-plugin - 2.0.0 + 2.5.2 springcloud/openjdk diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index d2a4a00b..a89b7800 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -21,8 +21,9 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; -import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; @@ -53,54 +54,56 @@ public class SpringCloudStreamAppGeneratorMojoTest { private Class mojoClazz = springCloudStreamAppMojo.getClass(); + private SpringCloudStreamAppGeneratorMojo.Application application; + @Before public void before() throws NoSuchFieldException { - SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); - containerImage.setFormat(AppDefinition.ContainerImageFormat.Docker); + 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"); - setMojoProperty("containerImage", containerImage); + application.getContainerImage().setFormat(AppDefinition.ContainerImageFormat.Docker); - SpringCloudStreamAppGeneratorMojo.GeneratedApp generatedApp = new SpringCloudStreamAppGeneratorMojo.GeneratedApp(); - generatedApp.setName("log"); - generatedApp.setType(AppDefinition.AppType.sink); - generatedApp.setVersion("3.0.0.BUILD-SNAPSHOT"); - generatedApp.setConfigClass("io.pivotal.java.function.log.consumer.LogConsumerConfiguration.class"); + application.getMetadata().getSourceTypeFilters().add("io.pivotal.java.function.log.consumer.LogConsumerProperties"); + application.getMetadata().getNameFilters().add("server.port"); - setMojoProperty("generatedApp", generatedApp); - - setMojoProperty("metadataSourceTypeFilters", Arrays.asList("io.pivotal.java.function.log.consumer.LogConsumerProperties")); - setMojoProperty("metadataNameFilters", Arrays.asList("server.port")); - - setMojoProperty("additionalAppProperties", Arrays.asList( - "spring.cloud.streamapp.security.enabled=false", - "spring.cloud.streamapp.security.csrf-enabled=false")); + application.getProperties().add("spring.cloud.streamapp.security.enabled=false"); + application.getProperties().add("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"); - setMojoProperty("dependencies", Arrays.asList(dep)); + 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(); - kafkaBinder.setName("kafka"); 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(); - rabbitBinder.setName("rabbit"); Dependency rabbitDep = new Dependency(); rabbitDep.setGroupId("org.springframework.cloud"); rabbitDep.setArtifactId("spring-cloud-stream-binder-rabbit"); rabbitBinder.getMaven().getDependencies().add(rabbitDep); - setMojoProperty("binders", Arrays.asList(kafkaBinder, rabbitBinder)); + Map binders = new HashMap<>(); + binders.put("kafka", kafkaBinder); + binders.put("rabbit", rabbitBinder); - // BOM - setMojoProperty("bootVersion", "2.3.0.M1"); - setMojoProperty("appMetadataMavenPluginVersion", "1.0.2.BUILD-SNAPSHOT"); + setMojoProperty("binders", binders); //setMojoProperty("generatedProjectHome", "./target/apps"); setMojoProperty("generatedProjectHome", projectHome.getRoot().getAbsolutePath()); @@ -110,11 +113,9 @@ public class SpringCloudStreamAppGeneratorMojoTest { public void testWithDisabledContainerMetadata() throws Exception { // disable metadata in container image (Default) - SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); - containerImage.setEnableMetadata(false); - setMojoProperty("containerImage", containerImage); + application.getContainerImage().setEnableMetadata(false); - this.springCloudStreamAppMojo.execute(); + springCloudStreamAppMojo.execute(); //Model pomModel = getModel(new File("./target/apps")); Model pomModel = getModel(new File(projectHome.getRoot().getAbsolutePath())); @@ -136,17 +137,15 @@ public class SpringCloudStreamAppGeneratorMojoTest { public void testDefaultProjectCreationByPlugin() throws Exception { // Enable Metadata in Container Image! - SpringCloudStreamAppGeneratorMojo.ContainerImage containerImage = new SpringCloudStreamAppGeneratorMojo.ContainerImage(); - containerImage.setEnableMetadata(true); - setMojoProperty("containerImage", containerImage); + application.getContainerImage().setEnableMetadata(true); - this.springCloudStreamAppMojo.execute(); + springCloudStreamAppMojo.execute(); //assertGeneratedPomXml(new File("./target/apps")); assertGeneratedPomXml(new File(projectHome.getRoot().getAbsolutePath())); } - private void assertGeneratedPomXml(File rootPath) throws Exception { + private void assertGeneratedPomXml(File rootPath) { Model pomModel = getModel(rootPath); @@ -197,8 +196,8 @@ public class SpringCloudStreamAppGeneratorMojoTest { } private void setMojoProperty(String propertyName, Object value) throws NoSuchFieldException { - Field mojoProperty = this.mojoClazz.getDeclaredField(propertyName); + Field mojoProperty = mojoClazz.getDeclaredField(propertyName); mojoProperty.setAccessible(true); - ReflectionUtils.setField(mojoProperty, this.springCloudStreamAppMojo, value); + ReflectionUtils.setField(mojoProperty, springCloudStreamAppMojo, value); } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index fe90de10..d8638b8d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -40,137 +40,144 @@ - - - ${spring-boot.version} - - - - kafka + + + ${spring-boot.version} + + 2.0.2.BUILD-SNAPSHOT + - + org.springframework.cloud - spring-cloud-stream-binder-kafka + spring-cloud-stream-dependencies + ${spring-cloud-stream-dependencies.version} - - - - - rabbit - - org.springframework.cloud - spring-cloud-stream-binder-rabbit + spring-cloud-function-dependencies + ${spring-cloud-function-dependencies.version} + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + + + + + org.springframework.cloud.stream.app + stream-apps-security-common + ${stream-apps-core.version} + + + org.springframework.cloud.stream.app + stream-apps-micrometer-common + ${stream-apps-core.version} + + + io.micrometer + micrometer-registry-influx + + + io.micrometer + micrometer-registry-prometheus + + + io.micrometer + micrometer-registry-datadog + + + io.pivotal.cfenv + java-cfenv-boot + ${java-cfenv-boot.version} + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-security + + + org.logaritex + test-additional-maven-plugin + 3.0.0 + + + springcloud/openjdk + + + springcloudstream:${project.artifactId} + + latest + + + + USE_CURRENT_TIMESTAMP + Docker + + ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} + + + ]]> + + + + sign-artifacts + verify + + sign + + + + + - - - - - org.springframework.cloud - spring-cloud-stream-dependencies - ${spring-cloud-stream-dependencies.version} - - - org.springframework.cloud - spring-cloud-function-dependencies - ${spring-cloud-function-dependencies.version} - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud-dependencies.version} - - + + + + + + + org.springframework.cloud + spring-cloud-stream-binder-kafka + + + + + + + + + org.springframework.cloud + spring-cloud-stream-binder-rabbit + + + + + - - - org.springframework.cloud.stream.app - stream-apps-security-common - ${stream-apps-core.version} - - - org.springframework.cloud.stream.app - stream-apps-micrometer-common - ${stream-apps-core.version} - - - io.micrometer - micrometer-registry-influx - - - io.micrometer - micrometer-registry-prometheus - - - io.micrometer - micrometer-registry-datadog - - - io.pivotal.cfenv - java-cfenv-boot - ${java-cfenv-boot.version} - - - org.springframework.boot - spring-boot-configuration-processor - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-logging - - - org.springframework.boot - spring-boot-starter-security - - - - - org.logaritex - test-additional-maven-plugin - 3.0.0 - - - springcloud/openjdk - - - springcloudstream:${project.artifactId} - - latest - - - - USE_CURRENT_TIMESTAMP - Docker - - ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} - - - ]]> - - - - sign-artifacts - verify - - sign - - - - - + + + + @@ -184,42 +191,43 @@ ./target/apps - - - testspringcloud - true - - - + http source ${project.version} io.pivotal.java.function.http.supplier.HttpSupplierConfiguration.class - - - - io.pivotal.java.function - http-supplier - ${java-functions.version} - - + + spring.main.web-application-type=reactive + spring.cloud.streamapp.security.enabled=false + spring.cloud.streamapp.security.csrf-enabled=false + - - spring.main.web-application-type=reactive - spring.cloud.streamapp.security.enabled=false - spring.cloud.streamapp.security.csrf-enabled=false - + + testspringcloud + true + - - io.pivotal.java.function.http.supplier.HttpSourceProperties - io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors - - - - server.port - + + + + io.pivotal.java.function + http-supplier + ${java-functions.version} + + + + + + io.pivotal.java.function.http.supplier.HttpSourceProperties + io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors + + + server.port + + + @@ -242,5 +250,4 @@ - From 8add0f59acd73f96c7c9d047ea1ac067cd11318f Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 20 Aug 2020 13:44:26 -0400 Subject: [PATCH 07/76] Migrating the metadata plugin from the old location Old location: https://github.com/spring-cloud/spring-cloud-app-starters-maven-plugins/tree/master/spring-cloud-app-starter-metadata-maven-plugin --- pom.xml | 1 + .../README.adoc | 95 +++ .../pom.xml | 175 +++++ .../app/plugin/MetadataAggregationMojo.java | 614 ++++++++++++++++++ .../app/plugin/EnumHintProviderTest.java | 56 ++ .../plugin/MergeWhitelistPropertiesTest.java | 58 ++ .../cloud/dataflow/completion/Expresso.java | 25 + .../spring-configuration-metadata.json | 26 + .../resources/META-INF/whitelist-1.properties | 2 + .../resources/META-INF/whitelist-2.properties | 25 + 10 files changed, 1077 insertions(+) create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/README.adoc create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/pom.xml create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/EnumHintProviderTest.java create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MergeWhitelistPropertiesTest.java create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/completion/Expresso.java create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/spring-configuration-metadata.json create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-1.properties create mode 100644 spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-2.properties diff --git a/pom.xml b/pom.xml index abe7053c..edce1e87 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ spring-cloud-dataflow-apps-generator-plugin + spring-cloud-dataflow-apps-metadata-plugin diff --git a/spring-cloud-dataflow-apps-metadata-plugin/README.adoc b/spring-cloud-dataflow-apps-metadata-plugin/README.adoc new file mode 100644 index 00000000..4920beb5 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/README.adoc @@ -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): +``` + + + + org.springframework.cloud.stream.app.plugin + spring-cloud-app-starter-metadata-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + + aggregate-metadata + compile + + aggregate-metadata + + + + + + +``` + +This will produce and attach to the build a jar file named `--metadata.jar` (`metadata` being the +maven classifier used for the artifact, which can be customized as such:) +``` + + + + org.springframework.cloud.stream.app.plugin + spring-cloud-app-starter-metadata-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + + aggregate-metadata + compile + + aggregate-metadata + + + foobar + + + + + + +``` + +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: +``` + + + + org.springframework.cloud + spring-cloud-app-starter-metadata-maven-plugin + 2.0.0.BUILD-SNAPSHOT + + + true + + + + server.port + + + io.pivotal.java.function.http.supplier.HttpSourceProperties + io.pivotal.java.function.http.supplier.HttpSourceProperties$Cors + + + + + + + aggregate-metadata + compile + + aggregate-metadata + + + + + + +``` + diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml new file mode 100644 index 00000000..5c87d922 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -0,0 +1,175 @@ + + + 4.0.0 + + spring-cloud-dataflow-apps-metadata-plugin + 1.0.0-SNAPSHOT + maven-plugin + + + org.springframework.cloud + spring-cloud-dataflow-apps-plugin-parent + 1.0.0-SNAPSHOT + .. + + + + 3.6.3 + 4.12 + 3.11.1 + 1.8 + 5.2.8.RELEASE + 1.26 + + + + + org.apache.maven + maven-plugin-api + ${maven.version} + provided + + + org.springframework + spring-core + ${spring.version} + + + org.springframework + spring-beans + ${spring.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + org.apache.maven + maven-core + ${maven.version} + + + org.springframework.boot + spring-boot-configuration-processor + 2.3.2.RELEASE + + + io.rsocket + rsocket-core + 1.0.1 + + + org.springframework.boot + spring-boot-loader + 2.3.2.RELEASE + + + junit + junit + ${junit.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.4 + provided + + + org.apache.commons + commons-text + ${commons-text.version} + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + spring + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + + + false + + spring-releases + Spring Releases + https://repo.spring.io/release + + + + false + + spring-libs-release + Spring Libs Release + https://repo.spring.io/libs-release + + + + false + + spring-milestone-release + Spring Milestone Release + https://repo.spring.io/libs-milestone + + + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + + + + diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java new file mode 100644 index 00000000..fefb3102 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java @@ -0,0 +1,614 @@ +/* + * 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"; + + @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. + storeInboutOutboundPortMappingConfigurations(result.visible); + } + + /** + * Store pre-filtered and json-escaped metadata into a property file. + */ + private void storeFilteredMetadata() throws MojoExecutionException { + File targetFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF"); + if (!targetFolder.exists()) { + targetFolder.mkdir(); + } + try (FileWriter fileWriter = new FileWriter( + new File(targetFolder, "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 storeInboutOutboundPortMappingConfigurations(Properties properties) throws MojoExecutionException { + File targetFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF"); + if (!targetFolder.exists()) { + targetFolder.mkdir(); + } + try (FileWriter fileWriter = new FileWriter( + new File(targetFolder, SPRING_CLOUD_DATAFLOW_PORT_MAPPING_PROPERTIES))) { + for (String propertyKey : properties.stringPropertyNames()) { + if (propertyKey.equals(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS)) { + fileWriter.write(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS + "=" + properties + .getProperty(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS)); + fileWriter.write(System.lineSeparator()); + } + else if (propertyKey.equals(CONFIGURATION_PROPERTIES_INBOUND_PORTS)) { + fileWriter.write(CONFIGURATION_PROPERTIES_INBOUND_PORTS + "=" + properties + .getProperty(CONFIGURATION_PROPERTIES_INBOUND_PORTS)); + fileWriter.write(System.lineSeparator()); + } + } + } + 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 inboundPorts = new ArrayList<>(); + List 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 = 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 sourceTypeFilters = CollectionUtils.isEmpty(metadataFilters.getSourceTypes()) ? + Collections.EMPTY_LIST : metadataFilters.getSourceTypes(); + + List nameFilters = CollectionUtils.isEmpty(metadataFilters.getNames()) ? + Collections.EMPTY_LIST : metadataFilters.getNames(); + + ConfigurationMetadata filteredMetadata = new ConfigurationMetadata(); + List 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 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 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"); + + 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> providers = new HashMap<>(); + + Map 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 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()); + } + + //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 names; + + private List sourceTypes; + + public List getNames() { + return names; + } + + public void setNames(List names) { + this.names = names; + } + + public List getSourceTypes() { + return sourceTypes; + } + + public void setSourceTypes(List 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 Result(ConfigurationMetadata metadata, Properties visible) { + this.metadata = metadata; + this.visible = visible; + } + } +} diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/EnumHintProviderTest.java b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/EnumHintProviderTest.java new file mode 100644 index 00000000..4acea368 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/EnumHintProviderTest.java @@ -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); + + } +} diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MergeWhitelistPropertiesTest.java b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MergeWhitelistPropertiesTest.java new file mode 100644 index 00000000..1e025415 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MergeWhitelistPropertiesTest.java @@ -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); + } + +} diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/completion/Expresso.java b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/completion/Expresso.java new file mode 100644 index 00000000..95e04f68 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/java/org/springframework/cloud/dataflow/completion/Expresso.java @@ -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; +} diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/spring-configuration-metadata.json b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 00000000..d8ff7ea1 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/spring-configuration-metadata.json @@ -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": [ + ] +} diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-1.properties b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-1.properties new file mode 100644 index 00000000..0fbbde32 --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-1.properties @@ -0,0 +1,2 @@ +configuration-properties.classes=\ + org.springframework.cloud.stream.app.tasklaunchrequest.DataflowTaskLaunchRequestProperties diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-2.properties b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-2.properties new file mode 100644 index 00000000..ce4f979a --- /dev/null +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/test/resources/META-INF/whitelist-2.properties @@ -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 From b97dde186e023cfc455b3796a1e678b7f9462586 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 20 Aug 2020 14:41:16 -0400 Subject: [PATCH 08/76] Migrating the docs plugin Old location: https://github.com/spring-cloud/spring-cloud-app-starters-maven-plugins/tree/master/spring-cloud-app-starter-doc-maven-plugin --- pom.xml | 1 + .../README.adoc | 44 +++ .../pom.xml | 102 +++++ ...onfigurationMetadataDocumentationMojo.java | 359 ++++++++++++++++++ ...urationMetadataDocumentationMojoTests.java | 38 ++ 5 files changed, 544 insertions(+) create mode 100644 spring-cloud-dataflow-apps-docs-plugin/README.adoc create mode 100644 spring-cloud-dataflow-apps-docs-plugin/pom.xml create mode 100644 spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java create mode 100644 spring-cloud-dataflow-apps-docs-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojoTests.java diff --git a/pom.xml b/pom.xml index edce1e87..eb3dbf9d 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ spring-cloud-dataflow-apps-generator-plugin spring-cloud-dataflow-apps-metadata-plugin + spring-cloud-dataflow-apps-docs-plugin diff --git a/spring-cloud-dataflow-apps-docs-plugin/README.adoc b/spring-cloud-dataflow-apps-docs-plugin/README.adoc new file mode 100644 index 00000000..6d68859e --- /dev/null +++ b/spring-cloud-dataflow-apps-docs-plugin/README.adoc @@ -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[] +//enc::configuration-properties[] +``` + + +Then, configure this plugin for your app project (either directly or through a parent POM): +``` + + + + org.springframework.cloud.stream.app.plugin + spring-cloud-stream-app-documentation-maven-plugin + 1.0.0.BUILD-SNAPSHOT + + + generate-documentation + verify + + generate-documentation + + + + + + +``` + +Documentation for the visible properties shall appear on next build, which should be committed under VCS. + diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml new file mode 100644 index 00000000..8e316985 --- /dev/null +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-dataflow-apps-plugin-parent + 1.0.0-SNAPSHOT + .. + + + spring-cloud-dataflow-apps-docs-plugin + maven-plugin + + + + org.apache.maven + maven-plugin-api + 3.5.2 + + + org.apache.maven + maven-core + 3.5.2 + + + org.springframework.cloud + spring-cloud-dataflow-configuration-metadata + 2.7.0-SNAPSHOT + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.4 + provided + + + + junit + junit + 4.12 + test + + + org.assertj + assertj-core + 3.15.0 + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + spring + + + + true + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + + + false + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + + + false + + spring-releases + Spring Releases + https://repo.spring.io/release + + + + + diff --git a/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java b/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java new file mode 100644 index 00000000..01181d82 --- /dev/null +++ b/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java @@ -0,0 +1,359 @@ +/* + * 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.List; +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. + * + * @author Eric Bottard + * @author David Turanski + * @see
Whitelisting + * Properties + */ +@Mojo(name = "generate-documentation", requiresDependencyResolution = ResolutionScope.RUNTIME) +public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { + + private BootApplicationConfigurationMetadataResolver metadataResolver = + new BootApplicationConfigurationMetadataResolver(imageName -> null); + + @Parameter(defaultValue = "${project}") + private MavenProject mavenProject; + + @Parameter(defaultValue = "false") + private boolean failOnMissingDescription; + + 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 = null; + do { + line = reader.readLine(); + out.println(line); + } + while (line != null && !line.startsWith("//tag::configuration-properties[]")); + if (line == null) { + getLog().info("No documentation section marker found"); + return; + } + + ScatteredArchive archive = new ScatteredArchive(mavenProject); + BootClassLoaderFactory bootClassLoaderFactory = new BootClassLoaderFactory(archive, null); + try (URLClassLoader classLoader = bootClassLoaderFactory.createClassLoader()) { + debug(classLoader); + + List properties = metadataResolver.listProperties(archive, false); + Collections.sort(properties, new Comparator() { + + @Override + public int compare(ConfigurationMetadataProperty p1, ConfigurationMetadataProperty p2) { + return p1.getId().compareTo(p2.getId()); + } + }); + + for (ConfigurationMetadataProperty property : properties) { + getLog().debug("Documenting " + property.getId()); + out.println(asciidocFor(property, classLoader)); + } + + do { + line = reader.readLine(); + // drop lines + } + while (!line.startsWith("//end::configuration-properties[]")); + + // 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 debug(ClassLoader classLoader) { + if (classLoader instanceof URLClassLoader) { + List 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) { + return String.format("$$%s$$:: $$%s$$ *($$%s$$, default: `$$%s$$`%s)*", + property.getId(), + niceDescription(property), + niceType(property), + niceDefault(property), + maybeHints(property, classLoader)); + } + + private String niceDescription(ConfigurationMetadataProperty property) { + if (property.getDescription() == null) { + if (failOnMissingDescription) { + throw new RuntimeException("Missing description for property " + property.getId()); + } + else { + return ""; + } + } + 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 ""; + } + else if ("".equals(property.getDefaultValue())) { + return ""; + } + 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 ""; + } + return niceType(type); + } + + String niceType(String type) { + List 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, type.length()); + } + + /** + * 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() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public List getNestedArchives(EntryFilter ignored) throws IOException { + try { + List 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 iterator() { + // BootClassLoaderFactory.createClassLoader (which uses this iterator call) is not + // actually + // used here. Returning the simples thing that works. + return Collections.emptyIterator(); + } + } +} diff --git a/spring-cloud-dataflow-apps-docs-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojoTests.java b/spring-cloud-dataflow-apps-docs-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojoTests.java new file mode 100644 index 00000000..f5b72ced --- /dev/null +++ b/spring-cloud-dataflow-apps-docs-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojoTests.java @@ -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>>"); + assertThat(s).isEqualTo("Entry>>"); + } + +} From ffc2afc9a3c937fe8214a5c662aac46e23f0c20f Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 20 Aug 2020 21:23:09 +0200 Subject: [PATCH 09/76] Add support for customizable maven repsotiries at app, binder and global levels --- .../SpringCloudStreamAppGeneratorMojo.java | 65 ++++++++--------- .../app/plugin/generator/AppDefinition.java | 34 +-------- .../plugin/generator/BinderDefinition.java | 47 +----------- .../app/plugin/generator/MavenDefinition.java | 71 +++++++++++++++++++ .../src/main/resources/template/app-pom.xml | 10 +++ .../dataflow/app/plugin/MojoHarnessTest.java | 6 +- .../resources/unit/http-source-apps/pom.xml | 30 ++++++++ 7 files changed, 151 insertions(+), 112 deletions(-) create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 2af6482f..23bc2ed8 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -32,6 +32,7 @@ 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.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -67,45 +68,15 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { @Parameter(defaultValue = "./apps", required = true) private String generatedProjectHome; - //@Parameter - //private ContainerImage containerImage = new ContainerImage(); - @Parameter(required = true) private Application application; - //@Parameter - //List additionalAppProperties; - - //@Parameter - //List metadataSourceTypeFilters = new ArrayList<>(); - // - //@Parameter - //List metadataNameFilters = new ArrayList<>(); - - //@Parameter - //List boms = new ArrayList<>(); - - //@Parameter - //List dependencies = new ArrayList<>(); - @Parameter Global global = new Global(); - //@Parameter - //List globalDependencies = new ArrayList<>(); - // - //@Parameter - //List additionalPlugins = new ArrayList<>(); @Parameter Map binders = new HashMap<>(); - // Versions - //@Parameter(defaultValue = "2.3.3", required = true) - //private String bootVersion; - -// @Parameter(defaultValue = "${app-metadata-maven-plugin-version}") -// private String appMetadataMavenPluginVersion; - @Override public void execute() throws MojoFailureException, MojoExecutionException { @@ -173,7 +144,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { allApplicationProperties.addAll(this.application.getProperties()); app.setProperties(allApplicationProperties); - // BOM + //Maven BOM List allManagedDependencies = new ArrayList<>(this.global.getApplication().getMaven().getDependencyManagement()); allManagedDependencies.addAll(this.application.getMaven().getDependencyManagement()); app.getMaven().setDependencyManagement(allManagedDependencies.stream() @@ -187,7 +158,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .map(xml -> MavenXmlWriter.indent(xml, 12)) .collect(Collectors.toList())); - // Dependencies + //Maven Dependencies List allDependencies = new ArrayList<>(this.global.getApplication().getMaven().getDependencies()); allDependencies.addAll(this.application.getMaven().getDependencies()); app.getMaven().setDependencies(allDependencies.stream() @@ -195,7 +166,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .map(xml -> MavenXmlWriter.indent(xml, 8)) .collect(Collectors.toList())); - // Plugins + //Maven Plugins List allPlugins = new ArrayList<>(this.global.getApplication().getMaven().getPlugins()); allPlugins.addAll(this.application.getMaven().getPlugins()); app.getMaven().setPlugins(allPlugins.stream() @@ -203,6 +174,15 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .map(d -> MavenXmlWriter.indent(d, 12)) .collect(Collectors.toList())); + // Maven Repositories + List allRepositories = new ArrayList<>(this.global.getApplication().getMaven().getRepositories()); + allRepositories.addAll(this.application.getMaven().getRepositories()); + app.getMaven().setRepositories(allRepositories.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(); @@ -239,7 +219,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .map(MavenXmlWriter::toXml) .map(xml -> MavenXmlWriter.indent(xml, 8)) .collect(Collectors.toList())); - bd.getMaven().setManagedDependencies( + bd.getMaven().setDependencyManagement( es.getValue().getMaven().getDependencyManagement().stream() .map(MavenXmlWriter::toXml) .map(xml -> MavenXmlWriter.indent(xml, 8)) @@ -249,6 +229,11 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .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())); @@ -436,6 +421,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } public static class Metadata { + private List sourceTypeFilters = new ArrayList<>(); private List nameFilters = new ArrayList<>(); private String mavenPluginVersion; @@ -467,6 +453,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } public static class ContainerImage { + private AppDefinition.ContainerImageFormat format = null; private String orgName = "springcloudstream"; private boolean enableMetadata = true; @@ -532,6 +519,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { private List plugins = new ArrayList<>(); + private List repositories = new ArrayList<>(); + public List getProperties() { return properties; } @@ -563,6 +552,14 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { public void setPlugins(List plugins) { this.plugins = plugins; } + + public List getRepositories() { + return repositories; + } + + public void setRepositories(List repositories) { + this.repositories = repositories; + } } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 6f88a530..718cee68 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -62,7 +62,7 @@ public class AppDefinition { /** * Maven related configurations to be applied for this App definition. */ - private final Maven maven = new Maven(); + private final MavenDefinition maven = new MavenDefinition(); /** * ContainerImage related configurations to be applied for this App definition. @@ -114,7 +114,7 @@ public class AppDefinition { this.properties = properties; } - public Maven getMaven() { + public MavenDefinition getMaven() { return maven; } @@ -180,36 +180,6 @@ public class AppDefinition { sink } - public static class Maven { - private List dependencyManagement = new ArrayList<>(); - private List dependencies = new ArrayList<>(); - private List plugins = new ArrayList<>(); - - public List getDependencyManagement() { - return dependencyManagement; - } - - public void setDependencyManagement(List dependencyManagement) { - this.dependencyManagement = dependencyManagement; - } - - public List getDependencies() { - return dependencies; - } - - public void setDependencies(List dependencies) { - this.dependencies = dependencies; - } - - public List getPlugins() { - return plugins; - } - - public void setPlugins(List plugins) { - this.plugins = plugins; - } - } - public static class ContainerImage { /** * Allow to generate either Docker or OCI image formats. diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java index 28d3fce1..6b52697a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java @@ -26,7 +26,7 @@ public class BinderDefinition { private String name; - private final Maven maven = new Maven(); + private final MavenDefinition maven = new MavenDefinition(); private List properties = new ArrayList<>(); @@ -38,7 +38,7 @@ public class BinderDefinition { this.name = name; } - public Maven getMaven() { + public MavenDefinition getMaven() { return maven; } @@ -49,47 +49,4 @@ public class BinderDefinition { public void setProperties(List properties) { this.properties = properties; } - - public static class Maven { - - private List properties = new ArrayList<>(); - - private List managedDependencies = new ArrayList<>(); - - private List dependencies = new ArrayList<>(); - - private List plugins = new ArrayList<>(); - - public List getProperties() { - return properties; - } - - public void setProperties(List properties) { - this.properties = properties; - } - - public List getManagedDependencies() { - return managedDependencies; - } - - public void setManagedDependencies(List managedDependencies) { - this.managedDependencies = managedDependencies; - } - - public List getDependencies() { - return dependencies; - } - - public void setDependencies(List dependencies) { - this.dependencies = dependencies; - } - - public List getPlugins() { - return plugins; - } - - public void setPlugins(List plugins) { - this.plugins = plugins; - } - } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java new file mode 100644 index 00000000..b13ca4ac --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java @@ -0,0 +1,71 @@ +/* + * 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; + +/** + * @author Christian Tzolov + */ +public class MavenDefinition { + private List properties = new ArrayList<>(); + private List dependencyManagement = new ArrayList<>(); + private List dependencies = new ArrayList<>(); + private List plugins = new ArrayList<>(); + private List repositories = new ArrayList<>(); + + public List getProperties() { + return properties; + } + + public void setProperties(List properties) { + this.properties = properties; + } + + public List getDependencyManagement() { + return dependencyManagement; + } + + public void setDependencyManagement(List dependencyManagement) { + this.dependencyManagement = dependencyManagement; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public List getPlugins() { + return plugins; + } + + public void setPlugins(List plugins) { + this.plugins = plugins; + } + + public List getRepositories() { + return repositories; + } + + public void setRepositories(List repositories) { + this.repositories = repositories; + } +} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index b679536c..72dec807 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -80,6 +80,16 @@ + + {{#app.maven.repositories}} + {{this}} + {{/app.maven.repositories}} + + {{#app-binder.maven.repositories}} + {{this}} + {{/app-binder.maven.repositories}} + + true diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index 1df9c626..c3114ecd 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -21,6 +21,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.stream.Collectors; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; @@ -98,7 +99,10 @@ public class MojoHarnessTest { assertThat(jibPlugin.getConfiguration().toString()).contains("testspringcloud/${project.artifactId}"); assertThat(jibPlugin.getConfiguration().toString()).contains("3.0.0.BUILD-SNAPSHOT"); - assertThat(pomModel.getRepositories().size()).isEqualTo(2); + 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) { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index d8638b8d..28bee67e 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -149,6 +149,16 @@ + + + + false + + bintray-global + bintray-global + https://dl.bintray.com/globbals/maven + + @@ -160,6 +170,16 @@ spring-cloud-stream-binder-kafka + + + + false + + bintray-binder + bintray-binder + https://dl.bintray.com/binder/maven + + @@ -216,6 +236,16 @@ ${java-functions.version} + + + + false + + bintray-application + bintray-app + https://dl.bintray.com/app/maven + + From 213d568184408c3bb1a9449ffc09696bf15b0955 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 10:45:30 +0200 Subject: [PATCH 10/76] Improve JavaDocs --- .../SpringCloudStreamAppGeneratorMojo.java | 205 ++++++++++++++---- .../app/plugin/generator/AppDefinition.java | 2 + .../plugin/generator/BinderDefinition.java | 11 + .../app/plugin/generator/MavenDefinition.java | 21 ++ .../generator/ProjectGeneratorProperties.java | 2 +- .../resources/unit/http-source-apps/pom.xml | 5 +- 6 files changed, 197 insertions(+), 49 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 23bc2ed8..d7260009 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -68,19 +68,24 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { @Parameter(defaultValue = "./apps", required = true) private String generatedProjectHome; + @Parameter + private Global global = new Global(); + @Parameter(required = true) private Application application; @Parameter - Global global = new Global(); - - @Parameter - Map binders = new HashMap<>(); + private Map 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)) { @@ -182,7 +187,6 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .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(); @@ -204,11 +208,21 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { // Generator Properties ProjectGeneratorProperties generatorProperties = new ProjectGeneratorProperties(); - Map allBinders = new HashMap<>(); - allBinders.putAll(this.global.getBinders()); + // ---------------------------------------------------------------------------------------------------------- + // Binders Configuration + // ---------------------------------------------------------------------------------------------------------- + + Map allBinders = new HashMap<>(this.global.getBinders()); + // Note: The Application concrete Binder definitions will replace any global + // Binder definitions with the same names. allBinders.putAll(this.binders); - generatorProperties.setBinders(allBinders.entrySet().stream() + if (CollectionUtils.isEmpty(allBinders)) { + throw new MojoExecutionException("At least one Binder configuration is required!"); + } + + // Converts Mojo's Binder configurations into BinderDefinitions. + List bindersDefinitions = allBinders.entrySet().stream() .map(es -> { BinderDefinition bd = new BinderDefinition(); bd.setName(es.getKey()); @@ -236,23 +250,28 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { return bd; }) - .collect(Collectors.toList())); + .collect(Collectors.toList()); + + // ---------------------------------------------------------------------------------------------------------- + // Project Generator + // ---------------------------------------------------------------------------------------------------------- - generatorProperties.setOutputFolder(new File(this.generatedProjectHome)); 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"); + throw new MojoFailureException("Project generation failure", e); } } /** - * If the visible metadata properties file is provided in the source project, add - * its type and name filters to the existing visible configurations. + * 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. @@ -262,7 +281,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { return; } Optional optionalProperties = - loadVisiblePropertiesFromResource(FileUtils.getFile(projectResourcesDir, "META-INF", VISIBLE_PROPERTIES_FILE_NAME)); + 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); @@ -271,7 +291,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { return; } - loadVisiblePropertiesFromResource(FileUtils.getFile(projectResourcesDir, "META-INF", DEPRECATED_WHITELIST_FILE_NAME)) + 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"); @@ -307,6 +328,9 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } } + /** + * + */ public static class Global { private final Application application = new Application(); @@ -322,23 +346,57 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } } + /** + * 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; - private String version; + + /** + * Application type. Cloud be of type (source, processor, sink). + */ private AppDefinition.AppType type; - private String configClass; - private String functionDefinition; + + /** + * 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; + + /** + * 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 List properties = new ArrayList<>(); - private final Maven maven = new Maven(); - + /** + * 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(); + public List getProperties() { return properties; } @@ -420,10 +478,29 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { 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 sourceTypeFilters = new ArrayList<>(); + + /** + * list of property names from application's configuration metadata to include in the visible metadata. + */ private List nameFilters = new ArrayList<>(); + + /** + * Version of the metadata maven plugin to be used. + */ private String mavenPluginVersion; public List getSourceTypeFilters() { @@ -450,48 +527,65 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { this.mavenPluginVersion = mavenPluginVersion; } } - } - public static class ContainerImage { + /** + * Configuration for injection the application's visible, configuration metadata into the Container image. + */ + public static class ContainerImage { - private AppDefinition.ContainerImageFormat format = null; - private String orgName = "springcloudstream"; - private boolean enableMetadata = true; + /** + * Target container image. Docker and OCI are supported. If not specified the Docker is selected. + */ + private AppDefinition.ContainerImageFormat format = null; - public AppDefinition.ContainerImageFormat getFormat() { - return format; - } + /** + * Target container image organization name. + */ + private String orgName = "springcloudstream"; - public void setFormat(AppDefinition.ContainerImageFormat format) { - this.format = format; - } + /** + * Enable or disable the inclusion of application's metadata into the image's labels. + */ + private boolean enableMetadata = true; - public String getOrgName() { - return orgName; - } + public AppDefinition.ContainerImageFormat getFormat() { + return format; + } - public void setOrgName(String orgName) { - this.orgName = orgName; - } + public void setFormat(AppDefinition.ContainerImageFormat format) { + this.format = format; + } - public boolean isEnableMetadata() { - return enableMetadata; - } + public String getOrgName() { + return orgName; + } - public void setEnableMetadata(boolean enableMetadata) { - this.enableMetadata = enableMetadata; + public void setOrgName(String orgName) { + this.orgName = orgName; + } + + public boolean isEnableMetadata() { + return enableMetadata; + } + + public void setEnableMetadata(boolean enableMetadata) { + this.enableMetadata = enableMetadata; + } } } + /** + * Binder specific properties to be applied in addition to the {@link Application} parameters. + */ public static class Binder { /** - * Application properties specific for the binder. + * Application properties specific for the binder. Contributed to the generated application.properties file. */ private List properties = new ArrayList<>(); /** - * Maven configuraiton specific for the binder. + * Binder specific maven configurations. Applied in addition to the {@link Application}'s maven configurations. */ private final Maven maven = new Maven(); @@ -509,16 +603,39 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } } + /** + * 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 List properties = new ArrayList<>(); + /** + * Custom maven management dependencies. Contributed to the generated POM's dependencyManagement section. + */ private List dependencyManagement = new ArrayList<>(); + /** + * Custom maven dependencies. Contributed to the generated POM's dependencies section. + */ private List 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 + *
@code{
+		 * 
+		 * }
section! Find more: {@link MavenXmlWriter#toXml(Plugin)}. + */ private List plugins = new ArrayList<>(); + /** + * Custom maven repositories. Contributed to the generated POM's repositories section. + */ private List repositories = new ArrayList<>(); public List getProperties() { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 718cee68..eee13d4a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; /** + * Application configurations used to parameterize the project templates. + * * @author Christian Tzolov */ public class AppDefinition { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java index 6b52697a..687df6a8 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/BinderDefinition.java @@ -20,14 +20,25 @@ 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 properties = new ArrayList<>(); public String getName() { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java index b13ca4ac..56d46fe2 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/MavenDefinition.java @@ -20,13 +20,34 @@ 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 properties = new ArrayList<>(); + + /** + * Contributes the dependencyManagement entries to the Maven's dependencyManagement dependencies. + */ private List dependencyManagement = new ArrayList<>(); + + /** + * Contributes the dependency entries to the Maven's dependencies section. + */ private List dependencies = new ArrayList<>(); + + /** + * Contributes the plugin entries to the Maven's plugins section. + */ private List plugins = new ArrayList<>(); + + /** + * Contributes the repository entries to the Maven's repositories section. + */ private List repositories = new ArrayList<>(); public List getProperties() { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java index b307ea4b..d2a9eb07 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGeneratorProperties.java @@ -64,7 +64,7 @@ public class ProjectGeneratorProperties { return binders; } - public void setBinders(List binders) { + public void setBinderDefinitions(List binders) { this.binders = binders; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 28bee67e..633acc4b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -161,6 +161,7 @@ + @@ -193,11 +194,7 @@ - - - - From bb656f6ae8a8b344959832f404d82b2b551193be Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 12:48:41 +0200 Subject: [PATCH 11/76] improve gen app README --- .../README.adoc | 331 ++++++++++-------- .../main/resources/doc/MojoConfiguration.jpg | Bin 0 -> 64594 bytes 2 files changed, 189 insertions(+), 142 deletions(-) create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/main/resources/doc/MojoConfiguration.jpg diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index fa199102..a479c9ba 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -1,6 +1,19 @@ = 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 @@ -8,155 +21,189 @@ 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] ---- - - org.springframework.cloud - spring-cloud-dataflow-apps-generator-plugin - 1.0.0-SNAPSHOT - - 1.8 - ${spring-boot.version} - ${app-metadata-maven-plugin-version} - - - app-starters-core-dependencies - org.springframework.cloud.stream.app - app-starters-core-dependencies - ${app-starters-core-dependencies.version} - - - - - - - - counter - sink - ${project.version} - io.pivotal.java.function.counter.consumer.CounterConsumerConfiguration.class - counterConsumer - - - spring.cloud.stream.propagateOriginalContentType=false - - - - io.pivotal.java.function - counter-consumer - ${java-functions.version} - - - - - org.springframework.cloud.stream.app - app-starters-security-common - - - org.springframework.cloud - spring-cloud-starter-config - - - org.springframework.cloud.stream.app - app-starters-micrometer-common - - - io.micrometer - micrometer-registry-influx - - - io.micrometer - micrometer-registry-prometheus - - - org.springframework.cloud - spring-cloud-cloudfoundry-connector - - - org.springframework.cloud - spring-cloud-spring-service-connector - - - io.micrometer.prometheus - prometheus-rsocket-spring - - - - + + + + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + 1.0.0-SNAPSHOT + + + app-gen + package + + generate-app + + + + + ${basedir}/apps + + + + + ${spring-boot.version} + + + true + + + + 2.0.2.BUILD-SNAPSHOT + + + + + + org.springframework.cloud.fn + function-dependencies + ${java-functions.version} + + + org.springframework.cloud + spring-cloud-stream-dependencies + ${spring-cloud-stream-dependencies.version} + + + org.springframework.cloud + spring-cloud-function-dependencies + ${spring-cloud-function-dependencies.version} + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + + + + + org.springframework.cloud.stream.app + stream-applications-security-common + + + org.springframework.cloud.stream.app + stream-applications-micrometer-common + + + org.springframework.cloud.stream.app + stream-applications-postprocessor-common + + + io.micrometer + micrometer-registry-prometheus + + + io.micrometer.prometheus + prometheus-rsocket-spring + + + io.micrometer + micrometer-registry-wavefront + + + io.pivotal.cfenv + java-cfenv-boot + + + org.springframework.boot + spring-boot-configuration-processor + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-security + + + + + + + + + + org.springframework.cloud + spring-cloud-stream-binder-kafka + + + + + + + + + org.springframework.cloud + spring-cloud-stream-binder-rabbit + + + + + + + + + + ---- +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] +---- + + + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + 1.0.0-SNAPSHOT + + + syslog + source + ${project.version} + org.springframework.cloud.fn.supplier.syslog.SyslogSupplierConfiguration.class + + + + + org.springframework.cloud.fn + syslog-supplier + + + org.springframework.cloud.stream.app + stream-applications-composite-function-support + ${stream-apps-core.version} + + + + + + + ... + +---- + + The `bom` for stream apps is specified in `spring-cloud-stream-app-dependencies` (group id: org.springframework.cloud.stream.app) and for tasks it is spring-cloud-task-app-dependencies (group id: org.springframework.cloud.task.app). -Extra dependency management can be added as additional `boms` to the plugin using the property -`-DbomsWithHigherPrecedence` and value based on the pattern `groupId:artfiactId:version`. This can - be a comma separated collection of boms. Any bom added like this will get priority in the order - they are specified over any default values used in the plugin configuration. - -Spring Boot version used in the generated app can be overridden using the plugin property - `-DbootVersion=`. - - -==== Copy Third-Party Resources -The `copyResources` configuration block permits copying Resources from third-party jar dependencies into the -generated App Starter classpath. - -Form example if you want to copy a truststore and a self-signed certificate (`clientKeyStore.jks`, `cacerts`) bundled inside the -`org.springframework.cloud.stream.app:coap-app-starters-common:2.0.0.BUILD-SNAPSHOT` dependency you can add this to the -`spring-cloud-dataflow-apps-generator-plugin` configuration: - -[source, xml] ----- - - org.springframework.cloud - spring-cloud-dataflow-apps-generator-plugin - - ${session.executionRootDirectory}/apps - ${project.version} - .... - - - - - - org.springframework.cloud.stream.app - coap-app-starters-common - 2.0.0.BUILD-SNAPSHOT - clientKeyStore.jks,cacerts - - - - ----- - -Later will add the following `maven-dependency-plugin` definition to the generated aps starter pom: - -[source, xml] ----- - - maven-dependency-plugin - - - - unpack - - - - - org.springframework.cloud.stream.app - coap-app-starters-common - 2.0.0.BUILD-SNAPSHOT - clientKeyStore.jks,cacerts - - - ${project.build.directory}/classes/ - - - - ----- - -This will ensure that the clientKeyStore.jks,cacerts files will be copied to the AppStarter's `BOOT-INF/classes/` ==== Insert additional properties into the application.properties file for the created app. The `additionalAppProperties` configuration block permits adding of additional properties to the application.properties file. diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/doc/MojoConfiguration.jpg b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/doc/MojoConfiguration.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2df425eab8914116750b0482a4ee6833e00f4b26 GIT binary patch literal 64594 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 literal 0 HcmV?d00001 From 6b6fbfbf9c839f6dc1febb8fb56659db3667cc5c Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 12:51:50 +0200 Subject: [PATCH 12/76] remove deprecated README content --- .../README.adoc | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index a479c9ba..f63d3600 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -199,31 +199,3 @@ to the global: ... ---- - - -The `bom` for stream apps is specified in `spring-cloud-stream-app-dependencies` (group id: org.springframework.cloud.stream.app) -and for tasks it is spring-cloud-task-app-dependencies (group id: org.springframework.cloud.task.app). - - -==== Insert additional properties into the application.properties file for the created app. -The `additionalAppProperties` configuration block permits adding of additional properties to the application.properties file. -[source, xml] ----- - - org.springframework.cloud - spring-cloud-dataflow-apps-generator-plugin - - ${session.executionRootDirectory}/apps - ${project.version} - .... - - - - - spring.cloud.task.closecontextEnabled=true - - - - ----- -In the example above we added the`spring.cloud.task.closecontextEnabled` property to the application.properties of the created app. From 281faa3716e37f04ee33b4ab3c777d6c54c68c05 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 15:17:03 +0200 Subject: [PATCH 13/76] [app gen plugin] fix app and maven property handling --- .../SpringCloudStreamAppGeneratorMojo.java | 78 ++++++++++++------- ...SpringCloudStreamAppGeneratorMojoTest.java | 4 +- .../resources/unit/http-source-apps/pom.xml | 19 ++++- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index d7260009..62aabc9e 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -33,6 +33,7 @@ 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; @@ -145,44 +146,65 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } // Application properties - List allApplicationProperties = new ArrayList<>(this.global.getApplication().getProperties()); - allApplicationProperties.addAll(this.application.getProperties()); - app.setProperties(allApplicationProperties); + Map allApplicationProperties = new HashMap<>(this.global.getApplication().getProperties()); + allApplicationProperties.putAll(this.application.getProperties()); + app.setProperties(allApplicationProperties.entrySet().stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.toList())); + + // Maven properties + Map allMavenProperties = new HashMap<>(this.global.getApplication().getMaven().getProperties()); + allMavenProperties.putAll(this.application.getMaven().getProperties()); + app.getMaven().setProperties(allMavenProperties.entrySet().stream() + .map(es -> "<" + es.getKey() + ">" + es.getValue() + "") + .collect(Collectors.toList())); //Maven BOM - List allManagedDependencies = new ArrayList<>(this.global.getApplication().getMaven().getDependencyManagement()); - allManagedDependencies.addAll(this.application.getMaven().getDependencyManagement()); - app.getMaven().setDependencyManagement(allManagedDependencies.stream() + Map allDependencyManagementMap = + this.global.getApplication().getMaven().getDependencyManagement().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. + allDependencyManagementMap.putAll(this.application.getMaven().getDependencyManagement().stream() + .collect(Collectors.toMap(d -> d.getGroupId() + ":" + d.getArtifactId(), d -> d))); + app.getMaven().setDependencyManagement(allDependencyManagementMap.values().stream() .filter(Objects::nonNull) - .map(dependency -> { + .peek(dependency -> { dependency.setScope("import"); dependency.setType("pom"); - return dependency; }) .map(MavenXmlWriter::toXml) .map(xml -> MavenXmlWriter.indent(xml, 12)) .collect(Collectors.toList())); //Maven Dependencies - List allDependencies = new ArrayList<>(this.global.getApplication().getMaven().getDependencies()); - allDependencies.addAll(this.application.getMaven().getDependencies()); - app.getMaven().setDependencies(allDependencies.stream() + Map 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 - List allPlugins = new ArrayList<>(this.global.getApplication().getMaven().getPlugins()); - allPlugins.addAll(this.application.getMaven().getPlugins()); - app.getMaven().setPlugins(allPlugins.stream() + Map 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 - List allRepositories = new ArrayList<>(this.global.getApplication().getMaven().getRepositories()); - allRepositories.addAll(this.application.getMaven().getRepositories()); - app.getMaven().setRepositories(allRepositories.stream() + Map 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())); @@ -214,7 +236,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { Map allBinders = new HashMap<>(this.global.getBinders()); // Note: The Application concrete Binder definitions will replace any global - // Binder definitions with the same names. + // 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)) { @@ -227,7 +249,10 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { BinderDefinition bd = new BinderDefinition(); bd.setName(es.getKey()); bd.setProperties(es.getValue().getProperties()); - bd.getMaven().setProperties(es.getValue().getMaven().getProperties()); + bd.getMaven().setProperties(es.getValue().getMaven().getProperties() + .entrySet().stream() + .map(pes -> "<" + pes.getKey() + ">" + pes.getValue() + "") + .collect(Collectors.toList())); bd.getMaven().setDependencies( es.getValue().getMaven().getDependencies().stream() .map(MavenXmlWriter::toXml) @@ -329,7 +354,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } /** - * + * Section that allow to define Application and Binder configurations common across multiple applications + * and binders. */ public static class Global { @@ -386,7 +412,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { /** * Custom application properties to contribute to the generated application.properties file. */ - private List properties = new ArrayList<>(); + private Map properties = new HashMap<>(); /** * Parameters used to tune the Application metadata generation. @@ -397,11 +423,11 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { private final Maven maven = new Maven(); - public List getProperties() { + public Map getProperties() { return properties; } - public void setProperties(List properties) { + public void setProperties(Map properties) { this.properties = properties; } @@ -611,7 +637,7 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { /** * Custom maven properties. Contributed to the generated POM's properties section. */ - private List properties = new ArrayList<>(); + private Map properties = new HashMap<>(); /** * Custom maven management dependencies. Contributed to the generated POM's dependencyManagement section. @@ -638,11 +664,11 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { */ private List repositories = new ArrayList<>(); - public List getProperties() { + public Map getProperties() { return properties; } - public void setProperties(List properties) { + public void setProperties(Map properties) { this.properties = properties; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index a89b7800..5160d78a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -70,8 +70,8 @@ public class SpringCloudStreamAppGeneratorMojoTest { application.getMetadata().getSourceTypeFilters().add("io.pivotal.java.function.log.consumer.LogConsumerProperties"); application.getMetadata().getNameFilters().add("server.port"); - application.getProperties().add("spring.cloud.streamapp.security.enabled=false"); - application.getProperties().add("spring.cloud.streamapp.security.csrf-enabled=false"); + 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"); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 633acc4b..4c176b1d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -46,7 +46,15 @@ 2.0.2.BUILD-SNAPSHOT + + value1 + value2 + + + value1 + value2 + org.springframework.cloud @@ -215,9 +223,11 @@ io.pivotal.java.function.http.supplier.HttpSupplierConfiguration.class - spring.main.web-application-type=reactive - spring.cloud.streamapp.security.enabled=false - spring.cloud.streamapp.security.csrf-enabled=false + reactive + false + false + + value2Updated @@ -226,6 +236,9 @@ + + value1 + io.pivotal.java.function From 60186aee60cf5e8fe972641ec6740978a586bb9f Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 15:46:50 +0200 Subject: [PATCH 14/76] document how to configure PubSub or Solace binder generation --- .../README.adoc | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index f63d3600..7d32102e 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -199,3 +199,63 @@ to the global: ... ---- + + +== 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] +---- + + .... + + + + + com.solace.spring.cloud + spring-cloud-starter-stream-solace + 1.2.1 + + + + + com.solace.spring.cloud + spring-cloud-starter-stream-solace + + + + + .... + +---- + +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] +---- + +... + + + + + org.springframework.cloud + spring-cloud-gcp-dependencies + 1.2.4.RELEASE + + + + + org.springframework.cloud + spring-cloud-gcp-pubsub-stream-binder + + + + +... + +---- From f1899d68f1c45ad05f7e8ab2e799ff4bc7eaa790 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 21 Aug 2020 16:42:34 +0200 Subject: [PATCH 15/76] Fix an issues with BOM generation --- spring-cloud-dataflow-apps-generator-plugin/README.adoc | 8 +------- .../app/plugin/SpringCloudStreamAppGeneratorMojo.java | 7 ++++++- .../src/main/resources/template/app-pom.xml | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index 7d32102e..72ea17ad 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -211,17 +211,11 @@ To generate applications for https://github.com/SolaceProducts/spring-cloud-stre .... - - - com.solace.spring.cloud - spring-cloud-starter-stream-solace - 1.2.1 - - com.solace.spring.cloud spring-cloud-starter-stream-solace + 1.2.1 diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 62aabc9e..d53c6322 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -260,8 +260,13 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { .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, 8)) + .map(xml -> MavenXmlWriter.indent(xml, 12)) .collect(Collectors.toList())); bd.getMaven().setPlugins( es.getValue().getMaven().getPlugins().stream() diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 72dec807..1ff4dd94 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -46,13 +46,13 @@ - {{#app.maven.dependencyManagement}} - {{this}} - {{/app.maven.dependencyManagement}} - {{#app-binder.maven.dependencyManagement}} {{this}} {{/app-binder.maven.dependencyManagement}} + + {{#app.maven.dependencyManagement}} + {{this}} + {{/app.maven.dependencyManagement}} From 31bf78c13b60f431f335406af4f91e3ec67d8e38 Mon Sep 17 00:00:00 2001 From: David Turanski Date: Fri, 28 Aug 2020 11:41:36 -0400 Subject: [PATCH 16/76] Make base image a property - Fix and add test - Fix checkstyle - Update jib plugin version --- .../SpringCloudStreamAppGeneratorMojo.java | 17 +++++++++++++++++ .../app/plugin/generator/AppDefinition.java | 18 +++++++++++++++++- .../src/main/resources/template/app-pom.xml | 2 +- .../resources/template/apps-modules-pom.xml | 2 +- .../SpringCloudStreamAppGeneratorMojoTest.java | 2 ++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index d53c6322..8a9ec228 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -227,6 +227,13 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { 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(); @@ -574,6 +581,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { */ private String orgName = "springcloudstream"; + private String baseImage = "springcloud/openjdk"; + /** * Enable or disable the inclusion of application's metadata into the image's labels. */ @@ -602,6 +611,14 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { public void setEnableMetadata(boolean enableMetadata) { this.enableMetadata = enableMetadata; } + + public String getBaseImage() { + return baseImage; + } + + public void setBaseImage(String baseImage) { + this.baseImage = baseImage; + } } } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index eee13d4a..2550008b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -187,13 +187,19 @@ public class AppDefinition { * 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. + * 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/openjdk"; + public ContainerImageFormat getFormat() { return format; } @@ -225,11 +231,21 @@ public class AppDefinition { 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 sourceTypeFilters = new ArrayList<>(); + private List nameFilters = new ArrayList<>(); + private String mavenPluginVersion; public List getSourceTypeFilters() { diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 1ff4dd94..465ab12f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -147,7 +147,7 @@ 2.5.2 - springcloud/openjdk + {{app.containerImage.baseImage}} {{app.containerImage.orgName}}/${project.artifactId} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml index 87636a71..c82fd2f7 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml @@ -83,7 +83,7 @@ com.google.cloud.tools jib-maven-plugin - 2.0.0 + 2.5.2 diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index 5160d78a..dd11a264 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -66,6 +66,7 @@ public class SpringCloudStreamAppGeneratorMojoTest { 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"); @@ -181,6 +182,7 @@ public class SpringCloudStreamAppGeneratorMojoTest { .contains("" + "${org.springframework.cloud.dataflow.spring.configuration.metadata.json}" + ""); + assertThat(jibPlugin.getConfiguration().toString()).contains("base/image"); assertThat(pomModel.getRepositories().size()).isEqualTo(2); } From ec6e243d81169fd18f0b74742ef7067564df8e88 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Sat, 29 Aug 2020 20:04:03 +0200 Subject: [PATCH 17/76] Remove properties' outer quotes. Sort properties by key --- .../app/plugin/SpringCloudStreamAppGeneratorMojo.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 8a9ec228..63ead03b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -27,6 +27,7 @@ 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; @@ -146,17 +147,19 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } // Application properties - Map allApplicationProperties = new HashMap<>(this.global.getApplication().getProperties()); + Map allApplicationProperties = new TreeMap<>(this.global.getApplication().getProperties()); allApplicationProperties.putAll(this.application.getProperties()); app.setProperties(allApplicationProperties.entrySet().stream() - .map(e -> e.getKey() + "=" + e.getValue()) + .map(e -> e.getKey() + "=" + (StringUtils.hasText(e.getValue()) ? + e.getValue().replaceAll("^\"|\"$", "") : "")) .collect(Collectors.toList())); // Maven properties - Map allMavenProperties = new HashMap<>(this.global.getApplication().getMaven().getProperties()); + Map allMavenProperties = new TreeMap<>(this.global.getApplication().getMaven().getProperties()); allMavenProperties.putAll(this.application.getMaven().getProperties()); app.getMaven().setProperties(allMavenProperties.entrySet().stream() - .map(es -> "<" + es.getKey() + ">" + es.getValue() + "") + .map(es -> "<" + es.getKey() + ">" + (StringUtils.hasText(es.getValue()) ? + es.getValue().replaceAll("^\"|\"$", "") : "") + "") .collect(Collectors.toList())); //Maven BOM From b8c79c5ff2a115b8b6e3576857c3c1fcc1fd101d Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Mon, 31 Aug 2020 09:37:49 +0200 Subject: [PATCH 18/76] Document the base image parameter --- .../dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 63ead03b..0f5a73d8 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -584,6 +584,9 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { */ private String orgName = "springcloudstream"; + /** + * Base images to be used by the target container image. + */ private String baseImage = "springcloud/openjdk"; /** From 69a1da8511681de92f19ac59401ecf29926adc15 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Mon, 31 Aug 2020 16:24:55 -0400 Subject: [PATCH 19/76] New coordinates for metadata plugin spring-cloud-app-starter-metadata-maven-plugin --> spring-cloud-dataflow-apps-metadata-plugin --- .../src/main/resources/template/app-pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 465ab12f..cf2c927f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -198,7 +198,7 @@ org.springframework.cloud - spring-cloud-app-starter-metadata-maven-plugin + spring-cloud-dataflow-apps-metadata-plugin {{app.metadata.mavenPluginVersion}} {{#app.containerImage.enableMetadata}} From 9cd2331bc855f34a87077cc8e6babecfd5fd7248 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 2 Sep 2020 17:21:38 +0200 Subject: [PATCH 20/76] Fix bug with local vs global mojo parameters - local vs global baseImage and orgName precedences. Remove the default values. --- .../app/plugin/SpringCloudStreamAppGeneratorMojo.java | 4 ++-- .../cloud/dataflow/app/plugin/MojoHarnessTest.java | 2 +- .../src/test/resources/unit/http-source-apps/pom.xml | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 0f5a73d8..e3dba726 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -582,12 +582,12 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { /** * Target container image organization name. */ - private String orgName = "springcloudstream"; + private String orgName; /** * Base images to be used by the target container image. */ - private String baseImage = "springcloud/openjdk"; + private String baseImage; /** * Enable or disable the inclusion of application's metadata into the image's labels. diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index c3114ecd..f1fcd94b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -98,7 +98,7 @@ public class MojoHarnessTest { ""); assertThat(jibPlugin.getConfiguration().toString()).contains("testspringcloud/${project.artifactId}"); assertThat(jibPlugin.getConfiguration().toString()).contains("3.0.0.BUILD-SNAPSHOT"); - + assertThat(jibPlugin.getConfiguration().toString()).contains("globalBaseImage"); assertThat(pomModel.getRepositories().size()).isEqualTo(5); assertThat(pomModel.getRepositories().stream().map(r -> r.getId()).collect(Collectors.toList())) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 4c176b1d..116d8c14 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -46,6 +46,11 @@ 2.0.2.BUILD-SNAPSHOT + + globalOrgName + globalBaseImage + true + value1 value2 From 5b522a71566b9593be45e565a853f39ffe008038 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Mon, 14 Sep 2020 11:11:31 -0400 Subject: [PATCH 21/76] Use spring.cloud.function.definition --- .../src/main/resources/template/app.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties index 7e023518..887ffd81 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties @@ -4,7 +4,7 @@ info.app.description=@project.description@ info.app.version=@project.version@ management.endpoints.web.exposure.include=health,info,bindings -spring.cloud.stream.function.definition={{app.functionDefinition}} +spring.cloud.function.definition={{app.functionDefinition}} {{#app.consumer}} spring.cloud.stream.function.bindings.{{app.functionDefinition}}-in-0=input From bbb5edac05810e9bc3ef34fb28bd1744b0fdaccc Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Tue, 22 Sep 2020 11:27:29 +0200 Subject: [PATCH 22/76] update the default baseimage references --- .../cloud/dataflow/app/plugin/MavenXmlWriter.java | 2 +- .../cloud/dataflow/app/plugin/generator/AppDefinition.java | 2 +- .../src/test/resources/unit/http-source-apps/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java index 3eab70b3..15dc2f44 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java @@ -74,7 +74,7 @@ public final class MavenXmlWriter { * 2.0.0 * * springcloud/openjdk + * springcloud/baseimage:1.0.0 * * springcloudstream:${project.artifactId} * latest diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 2550008b..d8ddaeb3 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -198,7 +198,7 @@ public class AppDefinition { private String tag = "latest"; - private String baseImage = "springcloud/openjdk"; + private String baseImage = "springcloud/baseimage:1.0.0"; public ContainerImageFormat getFormat() { return format; diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 116d8c14..aed9b728 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -134,7 +134,7 @@ - springcloud/openjdk + springcloud/baseimage:1.0.0 springcloudstream:${project.artifactId} From f9bab442665ba833f47c0b5dde59d5ad5e8d8912 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 29 Sep 2020 15:39:30 -0400 Subject: [PATCH 23/76] 1.0.0-M1 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 4 ++-- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index eb3dbf9d..9e6ffd78 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M1 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 8e316985..a1697ec1 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M1 .. @@ -26,7 +26,7 @@ org.springframework.cloud spring-cloud-dataflow-configuration-metadata - 2.7.0-SNAPSHOT + 2.7.0-M1 diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 800d287d..36c5affc 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M1 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 5c87d922..08abe40e 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -3,13 +3,13 @@ 4.0.0 spring-cloud-dataflow-apps-metadata-plugin - 1.0.0-SNAPSHOT + 1.0.0-M1 maven-plugin org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M1 .. From c77b1388fb7842545ac5aca122be841c2c32c088 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 29 Sep 2020 15:42:05 -0400 Subject: [PATCH 24/76] Next version: 1.0.0-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9e6ffd78..eb3dbf9d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M1 + 1.0.0-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index a1697ec1..5e4be277 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M1 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 36c5affc..800d287d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M1 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 08abe40e..5c87d922 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -3,13 +3,13 @@ 4.0.0 spring-cloud-dataflow-apps-metadata-plugin - 1.0.0-M1 + 1.0.0-SNAPSHOT maven-plugin org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M1 + 1.0.0-SNAPSHOT .. From c6ff497d956ce48a16e6c4ebdac9f8e6bfe8757e Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 30 Sep 2020 10:33:40 +0200 Subject: [PATCH 25/76] Add image labels for application name and version - Add an application.name container image label with value of the ${project.artifactId} - Add an application.version container image label with value of the ${project.version} --- .../src/main/resources/template/app-pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index cf2c927f..c62878ef 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -163,6 +163,8 @@ ${org.springframework.cloud.dataflow.spring.configuration.metadata.json} + ${project.artifactId} + ${project.version} {{/app.containerImage.enableMetadata}} From 91f388b637e72f504da7ec9cbc4bdac967066fa2 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 15 Oct 2020 13:18:06 +0200 Subject: [PATCH 26/76] Retain the DependencyManagement order --- .../SpringCloudStreamAppGeneratorMojo.java | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index e3dba726..e5620568 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -162,14 +162,23 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { es.getValue().replaceAll("^\"|\"$", "") : "") + "") .collect(Collectors.toList())); - //Maven BOM - Map allDependencyManagementMap = - this.global.getApplication().getMaven().getDependencyManagement().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. - allDependencyManagementMap.putAll(this.application.getMaven().getDependencyManagement().stream() - .collect(Collectors.toMap(d -> d.getGroupId() + ":" + d.getArtifactId(), d -> d))); - app.getMaven().setDependencyManagement(allDependencyManagementMap.values().stream() + //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 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"); @@ -309,6 +318,11 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { } } + 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. From ca79cddac89b3c7945ad89ec1780c957c739d040 Mon Sep 17 00:00:00 2001 From: David Turanski Date: Thu, 15 Oct 2020 11:33:10 -0400 Subject: [PATCH 27/76] Enable grouping of properties --- ...onfigurationMetadataDocumentationMojo.java | 109 ++++++++++++++---- 1 file changed, 87 insertions(+), 22 deletions(-) diff --git a/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java b/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java index 01181d82..5371f78e 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java +++ b/spring-cloud-dataflow-apps-docs-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/ConfigurationMetadataDocumentationMojo.java @@ -31,7 +31,12 @@ 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; @@ -54,20 +59,25 @@ 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. + * 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 Whitelisting + * "https://dataflow.spring.io/docs/feature-guides/general/application-metadata/#whitelisting-properties">Whitelisting * Properties */ @Mojo(name = "generate-documentation", requiresDependencyResolution = ResolutionScope.RUNTIME) public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { - private BootApplicationConfigurationMetadataResolver metadataResolver = - new BootApplicationConfigurationMetadataResolver(imageName -> null); + 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; @@ -75,6 +85,8 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { @Parameter(defaultValue = "false") private boolean failOnMissingDescription; + private boolean grouped = true; + public void execute() throws MojoExecutionException { File readme = new File(mavenProject.getBasedir(), "README.adoc"); @@ -94,41 +106,52 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { try (PrintWriter out = new PrintWriter(tmp); BufferedReader reader = new BufferedReader(new FileReader(readme))) { - String line = null; + String line; do { line = reader.readLine(); out.println(line); } - while (line != null && !line.startsWith("//tag::configuration-properties[]")); + while (line != null && !line.startsWith(CONFIGURATION_PROPERTIES_START_TAG)); if (line == null) { getLog().info("No documentation section marker found"); return; } + Map 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 properties = metadataResolver.listProperties(archive, false); - Collections.sort(properties, new Comparator() { + Collections.sort(properties, Comparator.comparing(ConfigurationMetadataProperty::getId)); - @Override - public int compare(ConfigurationMetadataProperty p1, ConfigurationMetadataProperty p2) { - return p1.getId().compareTo(p2.getId()); - } - }); + Map> 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()); + }); - for (ConfigurationMetadataProperty property : properties) { - getLog().debug("Documenting " + property.getId()); - out.println(asciidocFor(property, classLoader)); } - + else { + listProperties(properties, out, classLoader, prop -> prop.getId()); + } do { line = reader.readLine(); // drop lines } - while (!line.startsWith("//end::configuration-properties[]")); + while (!line.startsWith(CONFIGURATION_PROPERTIES_END_TAG)); // Copy remaining lines, including //end::configuration-properties[] while (line != null) { @@ -149,7 +172,33 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { catch (IOException e) { throw new MojoExecutionException("Error moving tmp file to README.adoc", e); } + } + private void listProperties(List properties, PrintWriter out, + ClassLoader classLoader, + Function propertyValue) { + for (ConfigurationMetadataProperty property : properties) { + getLog().debug("Documenting " + property.getId()); + out.println(asciidocFor(property, classLoader, propertyValue)); + } + + } + + private Map> groupProperties( + List properties) { + Map> 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) { @@ -159,15 +208,31 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { } } - private String asciidocFor(ConfigurationMetadataProperty property, ClassLoader classLoader) { + private String asciidocFor(ConfigurationMetadataProperty property, ClassLoader classLoader, + Function propertyValue) { return String.format("$$%s$$:: $$%s$$ *($$%s$$, default: `$$%s$$`%s)*", - property.getId(), + propertyValue.apply(property), niceDescription(property), niceType(property), niceDefault(property), maybeHints(property, classLoader)); } + private String asciidocForGroup(String group) { + return "\n=== " + group + "\n"; + } + + private Map startTagAttributes(String startTag) { + String attrs = startTag.substring(startTag.indexOf('[') + 1, startTag.indexOf(']')); + Set set = StringUtils.commaDelimitedListToSet(attrs); + Map 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) { @@ -305,7 +370,7 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { private String unqualify(String type) { int lastDot = type.lastIndexOf('.'); int lastDollar = type.lastIndexOf('$'); - return type.substring(Math.max(lastDot, lastDollar) + 1, type.length()); + return type.substring(Math.max(lastDot, lastDollar) + 1); } /** @@ -328,7 +393,7 @@ public class ConfigurationMetadataDocumentationMojo extends AbstractMojo { } @Override - public Manifest getManifest() throws IOException { + public Manifest getManifest() { throw new UnsupportedOperationException(); } From bba1974582e51aef80b3890299f87a3e809d9847 Mon Sep 17 00:00:00 2001 From: David Turanski Date: Mon, 26 Oct 2020 15:12:23 -0400 Subject: [PATCH 28/76] Switch template to use jupiter. --- .../src/main/resources/template/AppTests.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/AppTests.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/AppTests.java index 2f926ed9..c10aa855 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/AppTests.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/AppTests.java @@ -16,12 +16,9 @@ package {{app-package-name}}; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) @SpringBootTest public class {{app-class-name}}Tests { From 268cfcef05488925763e956d24934f917c5eb4d6 Mon Sep 17 00:00:00 2001 From: David Turanski Date: Mon, 26 Oct 2020 15:16:52 -0400 Subject: [PATCH 29/76] Fix typo --- spring-cloud-dataflow-apps-docs-plugin/README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-docs-plugin/README.adoc b/spring-cloud-dataflow-apps-docs-plugin/README.adoc index 6d68859e..cbd50246 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-docs-plugin/README.adoc @@ -14,7 +14,7 @@ To use this plugin, simply add the following markers to your project `README.ado ``` //tag::configuration-properties[] -//enc::configuration-properties[] +//end::configuration-properties[] ``` From f0d2ef11d96e6cbd85101b716bb6a0d2bb83119d Mon Sep 17 00:00:00 2001 From: David Turanski Date: Wed, 28 Oct 2020 13:40:59 -0400 Subject: [PATCH 30/76] Fix javadoc plugin for JDK 11 --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb3dbf9d..79e42b7d 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ + 1.8 true @@ -105,7 +106,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + ${maven-surefire-plugin.version} **/*Tests.java From 9ae7a310714db0ad39d823b3979901d75cc4f035 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 12 Nov 2020 14:39:53 -0500 Subject: [PATCH 31/76] 1.0.0-M2 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 79e42b7d..cf7539c5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M2 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 5e4be277..c6d79532 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M2 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 800d287d..da41c37a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M2 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 5c87d922..a742fe98 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -3,13 +3,12 @@ 4.0.0 spring-cloud-dataflow-apps-metadata-plugin - 1.0.0-SNAPSHOT maven-plugin org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-M2 .. From 323960d050d4a67866d7e5777cfc0dfcf0d4247d Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 12 Nov 2020 14:42:28 -0500 Subject: [PATCH 32/76] Next update version: 1.0.0-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index cf7539c5..79e42b7d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M2 + 1.0.0-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index c6d79532..5e4be277 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M2 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index da41c37a..800d287d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M2 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index a742fe98..7e1ffcc8 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-M2 + 1.0.0-SNAPSHOT .. From 82317169357695280e393800987e494e36c7b308 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Mon, 23 Nov 2020 15:54:18 -0500 Subject: [PATCH 33/76] Upgrade maven-jib-plugin to 2.6.0 Cleanup spring repo declarations --- pom.xml | 6 +++--- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 4 ++-- .../cloud/dataflow/app/plugin/MavenXmlWriter.java | 2 +- .../src/main/resources/template/app-pom.xml | 2 +- .../src/main/resources/template/apps-modules-pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 10 +++++----- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 79e42b7d..81be5f4c 100644 --- a/pom.xml +++ b/pom.xml @@ -189,12 +189,12 @@ repo.spring.io Spring Release Repository - https://repo.spring.io/libs-release-local + https://repo.spring.io/release repo.spring.io Spring Snapshot Repository - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot @@ -236,7 +236,7 @@ repo.spring.io Spring Milestone Repository - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 5e4be277..74b4f8a7 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -78,7 +78,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot @@ -86,7 +86,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java index 15dc2f44..74c424cb 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java @@ -71,7 +71,7 @@ public final class MavenXmlWriter { * * com.google.cloud.tools * jib-maven-plugin - * 2.0.0 + * 2.6.0 * * springcloud/baseimage:1.0.0 diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index c62878ef..ad226d4a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -144,7 +144,7 @@ com.google.cloud.tools jib-maven-plugin - 2.5.2 + 2.6.0 {{app.containerImage.baseImage}} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml index c82fd2f7..793e20a6 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml @@ -83,7 +83,7 @@ com.google.cloud.tools jib-maven-plugin - 2.5.2 + 2.6.0 diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 7e1ffcc8..7d8be5a5 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -116,7 +116,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot @@ -124,7 +124,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone @@ -148,7 +148,7 @@ spring-milestone-release Spring Milestone Release - https://repo.spring.io/libs-milestone + https://repo.spring.io/milestone @@ -158,7 +158,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot @@ -166,7 +166,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone From bec22854d4e1c995f82a6ca2bb0d860cd170026c Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 24 Nov 2020 12:07:33 -0500 Subject: [PATCH 34/76] 1.0.0-RC1 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 81be5f4c..56448546 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC1 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 74b4f8a7..57559cfa 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC1 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 800d287d..373bf159 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC1 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 7d8be5a5..9fa8ab02 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC1 .. From 6e6bb55f1dceab91c1c22e09b5fbff543dd24ff3 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 24 Nov 2020 12:13:51 -0500 Subject: [PATCH 35/76] Next update version: 1.0.0-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 56448546..81be5f4c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC1 + 1.0.0-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 57559cfa..74b4f8a7 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC1 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 373bf159..800d287d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC1 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 9fa8ab02..7d8be5a5 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC1 + 1.0.0-SNAPSHOT .. From 5785041f5fe50899c6ee3181517c9fbf73948226 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Fri, 4 Dec 2020 19:05:55 +0100 Subject: [PATCH 36/76] Fix application.properties override bug - Fix bug where the application.properties file in the original src/main/resource/ overrides the properties defined in the global and the app section. - Now the Global and Local defined properties are appended after any properties coming form the existing application.properties file. --- .../app/plugin/generator/ProjectGenerator.java | 10 ++++++---- .../cloud/dataflow/app/plugin/MojoHarnessTest.java | 10 ++++++++++ .../src/main/resources/application.properties | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/application.properties diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java index 872992f9..a3f3ea54 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -146,15 +146,17 @@ public final class ProjectGenerator { File appMainResourceDir = mkdirs(pkgToDir(appDir, "src.main.resources")); - // application.properties - copy(materialize("template/app.properties", appTemplateProperties), - file(appMainResourceDir, "application.properties")); - // 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); + copy(materialize("template/App.java", appTemplateProperties), file(appMainSrcDir, appClassName + ".java")); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index f1fcd94b..abb51609 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -18,9 +18,11 @@ 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; @@ -61,6 +63,14 @@ public class MojoHarnessTest { 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 dependencies = pomModel.getDependencies(); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/application.properties b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/application.properties new file mode 100644 index 00000000..42ca5767 --- /dev/null +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/src/main/resources/application.properties @@ -0,0 +1 @@ +static.property=bla From a24c9dbc94572a30196cd9c8468437fd60c40808 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Fri, 11 Dec 2020 15:27:16 -0500 Subject: [PATCH 37/76] 1.0.0-RC2 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 4 ++-- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 81be5f4c..62e8be35 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC2 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 74b4f8a7..950f94fc 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC2 .. @@ -26,7 +26,7 @@ org.springframework.cloud spring-cloud-dataflow-configuration-metadata - 2.7.0-M1 + 2.7.0 diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 800d287d..9aab915c 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC2 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 7d8be5a5..e5cb075f 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0-RC2 .. From b43dca6dbeaa76abc0879c83232c371f579b0f3e Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Fri, 11 Dec 2020 15:30:32 -0500 Subject: [PATCH 38/76] Back to 1.0.0-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 62e8be35..81be5f4c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC2 + 1.0.0-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 950f94fc..13fd8db9 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC2 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 9aab915c..800d287d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC2 + 1.0.0-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index e5cb075f..7d8be5a5 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-RC2 + 1.0.0-SNAPSHOT .. From 2a95dce4cddf83bf15625fc94104d53c8e7fce9c Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 13:08:50 -0500 Subject: [PATCH 39/76] 1.0.0 Release --- mvnw | 17 +++++++---------- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/mvnw b/mvnw index a08b219e..e1bf7a07 100755 --- a/mvnw +++ b/mvnw @@ -234,16 +234,13 @@ 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 - echo Deactivating \"milestone\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') -fi - -if echo $VERSION | egrep -q 'RELEASE'; then - echo Activating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" -else - echo Deactivating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') + if echo $VERSION | egrep -q 'SNAPSHOT'; then + echo Deactivating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') + else + echo Activating \"central\" profile for version=\"$VERSION\" + echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" + fi fi exec "$JAVACMD" \ diff --git a/pom.xml b/pom.xml index 81be5f4c..7c03ae88 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 13fd8db9..e607f945 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 800d287d..0811bdba 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 7d8be5a5..0445bac1 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0-SNAPSHOT + 1.0.0 .. From 15c700b767927fc133045aa841b1e47f02390ecf Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 13:31:48 -0500 Subject: [PATCH 40/76] Add missing project url, developers info --- pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pom.xml b/pom.xml index 7c03ae88..6a5df7be 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,19 @@ spring-cloud-dataflow-apps-metadata-plugin spring-cloud-dataflow-apps-docs-plugin + https://github.com/spring-cloud/spring-cloud-dataflow-apps-plugin + + + scdf-apps-plugin-developers + SCDF Apps Plugin Developers + chackos at vmware com + VMware + https://www.spring.io + + lead + + + From 361c66652bdd55c51daf426fa5bbe4080d145902 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 13:41:39 -0500 Subject: [PATCH 41/76] Next version: 1.0.1-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 6a5df7be..3527ec66 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0 + 1.0.1-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index e607f945..fb491b26 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0 + 1.0.1-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 0811bdba..39d7bd33 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0 + 1.0.1-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 0445bac1..74bcc8c9 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.0 + 1.0.1-SNAPSHOT .. From 6ccd6e312435424a96233a8a50d6ae9be28811e1 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 13:53:36 -0500 Subject: [PATCH 42/76] mvnw changes --- mvnw | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mvnw b/mvnw index e1bf7a07..c5e1a770 100755 --- a/mvnw +++ b/mvnw @@ -235,9 +235,13 @@ if echo $VERSION | egrep -q 'M|RC'; then 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 From fd43d9ba195a9a19f75f90ed4553ee9c65842e07 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 17:10:13 -0500 Subject: [PATCH 43/76] maven profile changes in mvnw --- .../src/main/resources/template/mvnw | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw index a08b219e..c5e1a770 100755 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/mvnw @@ -234,16 +234,17 @@ 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 - echo Deactivating \"milestone\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//') -fi - -if echo $VERSION | egrep -q 'RELEASE'; then - echo Activating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral" -else - echo Deactivating \"central\" profile for version=\"$VERSION\" - echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//') + 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" \ From c7a661cf8d750c07424bcf59785aa72cb55915a0 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 17:11:43 -0500 Subject: [PATCH 44/76] 1.0.1 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 3527ec66..fe9e5e41 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1-SNAPSHOT + 1.0.1 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index fb491b26..34e13c4a 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1-SNAPSHOT + 1.0.1 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 39d7bd33..39f92225 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1-SNAPSHOT + 1.0.1 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 74bcc8c9..fae9aa73 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1-SNAPSHOT + 1.0.1 .. From 8f13d499880ae402415b46b4416d02c8104eca6f Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 15 Dec 2020 17:27:08 -0500 Subject: [PATCH 45/76] Next version: 1.0.2-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index fe9e5e41..43b8879f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1 + 1.0.2-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 34e13c4a..bc07b02d 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1 + 1.0.2-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 39f92225..b1d23e4b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1 + 1.0.2-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index fae9aa73..0be8e138 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.1 + 1.0.2-SNAPSHOT .. From 70d7d403f4ade14cd98a0a3c719be20ee2a0fcdb Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 16 Dec 2020 13:57:07 -0500 Subject: [PATCH 46/76] Remove additional depedency for postprocessor common This is already provided for the apps through the core configuraton. --- .../src/main/resources/template/app-pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index ad226d4a..8b18219a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -67,11 +67,6 @@ {{this}} {{/app-binder.maven.dependencies}} - - org.springframework.cloud.stream.app - stream-applications-postprocessor-common - ${project.version} - org.springframework.boot spring-boot-starter-test From b1543e53266bc9d03e3d326a889b74414a64ccbc Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 16 Dec 2020 14:00:50 -0500 Subject: [PATCH 47/76] Fix tests --- .../cloud/dataflow/app/plugin/MojoHarnessTest.java | 2 +- .../app/plugin/SpringCloudStreamAppGeneratorMojoTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index abb51609..4bb85e16 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -74,7 +74,7 @@ public class MojoHarnessTest { Model pomModel = getModel(new File("./target/apps")); List dependencies = pomModel.getDependencies(); - assertThat(dependencies.size()).isEqualTo(15); + assertThat(dependencies.size()).isEqualTo(14); assertThat(dependencies.stream() .filter(d -> d.getArtifactId().equals("http-supplier")).count()).isEqualTo(1); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index dd11a264..8dc5c515 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -151,7 +151,7 @@ public class SpringCloudStreamAppGeneratorMojoTest { Model pomModel = getModel(rootPath); List dependencies = pomModel.getDependencies(); - assertThat(dependencies.size()).isEqualTo(4); + assertThat(dependencies.size()).isEqualTo(3); assertThat(dependencies.stream() .filter(d -> d.getArtifactId().equals("log-consumer")).count()).isEqualTo(1); From 44a04740ea014ddeafbc994784f5821525cc716b Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Wed, 16 Dec 2020 15:39:51 -0500 Subject: [PATCH 48/76] Fix tests --- .../cloud/dataflow/app/plugin/MojoHarnessTest.java | 2 +- .../app/plugin/SpringCloudStreamAppGeneratorMojoTest.java | 3 --- .../src/test/resources/unit/http-source-apps/pom.xml | 5 +++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index 4bb85e16..abb51609 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -74,7 +74,7 @@ public class MojoHarnessTest { Model pomModel = getModel(new File("./target/apps")); List dependencies = pomModel.getDependencies(); - assertThat(dependencies.size()).isEqualTo(14); + assertThat(dependencies.size()).isEqualTo(15); assertThat(dependencies.stream() .filter(d -> d.getArtifactId().equals("http-supplier")).count()).isEqualTo(1); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index 8dc5c515..9f776f25 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -159,9 +159,6 @@ public class SpringCloudStreamAppGeneratorMojoTest { assertThat(dependencies.stream() .filter(d -> d.getArtifactId().equals("spring-cloud-stream-binder-kafka")).count()).isEqualTo(1); - assertThat(dependencies.stream() - .filter(d -> d.getArtifactId().equals("stream-applications-postprocessor-common")).count()).isEqualTo(1); - Parent parent = pomModel.getParent(); assertThat(parent.getArtifactId()).isEqualTo("spring-boot-starter-parent"); assertThat(parent.getVersion()).isEqualTo("2.3.0.M1"); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index aed9b728..d0fbb19d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -88,6 +88,11 @@ stream-apps-micrometer-common ${stream-apps-core.version} + + org.springframework.cloud.stream.app + stream-applications-postprocessor-common + 3.0.0-SNAPSHOT + io.micrometer micrometer-registry-influx From 186810c34c55ba6c9bc992dc6f4352234592f70c Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 13 Jan 2021 17:33:22 +0100 Subject: [PATCH 49/76] Handle repetitive properties in generated application.properties file --- .../app/plugin/generator/ProjectGenerator.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java index a3f3ea54..04d2b644 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -18,15 +18,19 @@ 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; @@ -155,7 +159,17 @@ public final class ProjectGenerator { 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); + //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")); From a864172f7948c12517774d4e13c2a8e856771e0b Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 18 Feb 2021 13:56:44 -0500 Subject: [PATCH 50/76] 1.0.2 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 43b8879f..7f24a29f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2-SNAPSHOT + 1.0.2 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index bc07b02d..1b4be545 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2-SNAPSHOT + 1.0.2 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index b1d23e4b..bd61a551 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2-SNAPSHOT + 1.0.2 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 0be8e138..88370f7e 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2-SNAPSHOT + 1.0.2 .. From dd45c71c6130663efcaa90a53b7cf18d016163df Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 18 Feb 2021 14:03:34 -0500 Subject: [PATCH 51/76] Next version: 1.0.3-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7f24a29f..31b5630c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2 + 1.0.3-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 1b4be545..90f03acb 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2 + 1.0.3-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index bd61a551..34518fdd 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2 + 1.0.3-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 88370f7e..98ad114d 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.2 + 1.0.3-SNAPSHOT .. From d2622eb8bf202b5b6888011289091d445f40be73 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 27 May 2021 20:09:26 -0400 Subject: [PATCH 52/76] Hardcode maven-gpg-plugin version to 1.6 --- pom.xml | 1 + .../src/main/resources/template/app-pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 31b5630c..17077dd9 100644 --- a/pom.xml +++ b/pom.xml @@ -259,6 +259,7 @@ maven-gpg-plugin + 1.6 sign-artifacts diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 8b18219a..d83275fb 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -277,6 +277,7 @@ maven-gpg-plugin + 1.6 sign-artifacts From 600b145da4347a7362ac1e3c4eb262eb580f8549 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 27 May 2021 20:14:23 -0400 Subject: [PATCH 53/76] 1.0.3.RELEASE --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 17077dd9..511462ed 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3-SNAPSHOT + 1.0.3 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 90f03acb..571eeab6 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3-SNAPSHOT + 1.0.3 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 34518fdd..941855f6 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3-SNAPSHOT + 1.0.3 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 98ad114d..611a552b 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3-SNAPSHOT + 1.0.3 .. From 01fec955ce079aa8b9aac8580420661a02a390c7 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 27 May 2021 20:18:50 -0400 Subject: [PATCH 54/76] Next version: 1.0.4-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 511462ed..6210635e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3 + 1.0.4-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 571eeab6..a18876d0 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3 + 1.0.4-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 941855f6..b802fa0c 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3 + 1.0.4-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 611a552b..e33125c1 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.3 + 1.0.4-SNAPSHOT .. From acfe3058bec12acb306fac9e510e1d712bf332e3 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Wed, 1 Sep 2021 14:49:50 +0200 Subject: [PATCH 55/76] Add port mappings to metadata-jar - Ensure that the META-INF/dataflow-configuration-port-mapping.propertie is added to the app metadata jar as well. Resolves #6 --- .../app/plugin/MetadataAggregationMojo.java | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java index fefb3102..5377eec4 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java @@ -170,19 +170,21 @@ public class MetadataAggregationMojo extends AbstractMojo { storeFilteredMetadata(); } //Add port mapping configuration based on the application configuration. - storeInboutOutboundPortMappingConfigurations(result.visible); + storeInboundOutboundPortMappingConfigurations(result.getPortMappingProperties()); } /** * Store pre-filtered and json-escaped metadata into a property file. */ private void storeFilteredMetadata() throws MojoExecutionException { - File targetFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF"); - if (!targetFolder.exists()) { - targetFolder.mkdir(); + 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(targetFolder, "spring-configuration-metadata-encoded.properties"))) { + 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); @@ -192,25 +194,16 @@ public class MetadataAggregationMojo extends AbstractMojo { } } - private void storeInboutOutboundPortMappingConfigurations(Properties properties) throws MojoExecutionException { - File targetFolder = new File(mavenProject.getBuild().getOutputDirectory(), "META-INF"); - if (!targetFolder.exists()) { - targetFolder.mkdir(); + 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(targetFolder, SPRING_CLOUD_DATAFLOW_PORT_MAPPING_PROPERTIES))) { - for (String propertyKey : properties.stringPropertyNames()) { - if (propertyKey.equals(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS)) { - fileWriter.write(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS + "=" + properties - .getProperty(CONFIGURATION_PROPERTIES_OUTBOUND_PORTS)); - fileWriter.write(System.lineSeparator()); - } - else if (propertyKey.equals(CONFIGURATION_PROPERTIES_INBOUND_PORTS)) { - fileWriter.write(CONFIGURATION_PROPERTIES_INBOUND_PORTS + "=" + properties - .getProperty(CONFIGURATION_PROPERTIES_INBOUND_PORTS)); - fileWriter.write(System.lineSeparator()); - } - } + 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); @@ -497,6 +490,10 @@ public class MetadataAggregationMojo extends AbstractMojo { 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); + 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); } @@ -606,6 +603,14 @@ public class MetadataAggregationMojo extends AbstractMojo { 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; From 832bd3fd5229b8d38d6b49843337926ae4f59046 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 23 Sep 2021 12:44:06 +0200 Subject: [PATCH 56/76] Add option groups properties to generated metadata Related to https://github.com/spring-cloud/spring-cloud-dataflow/commit/0c805ec7875c4486fe14ce6c4083953c4a3bf5ee Related to https://github.com/spring-cloud/spring-cloud-dataflow/issues/4693 --- .../cloud/dataflow/app/plugin/MetadataAggregationMojo.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java index 5377eec4..d62faad2 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java +++ b/spring-cloud-dataflow-apps-metadata-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MetadataAggregationMojo.java @@ -106,6 +106,8 @@ public class MetadataAggregationMojo extends AbstractMojo { 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; @@ -492,6 +494,10 @@ public class MetadataAggregationMojo extends AbstractMojo { 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())); From 914f0a7bea34932c7e48b32f141616c8a2a1153c Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 23 Sep 2021 13:41:37 +0200 Subject: [PATCH 57/76] Upgrade mvn to 3.6.3 and wrapper to 0.5.6 Resolves #7 --- .mvn/wrapper/MavenWrapperDownloader.java | 2 +- .mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 4 +- mvnw | 198 +++++++++++++++-------- mvnw.cmd | 8 +- 5 files changed, 134 insertions(+), 78 deletions(-) diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index 720797dc..e76d1f32 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -20,7 +20,7 @@ import java.util.Properties; public class MavenWrapperDownloader { - private static final String WRAPPER_VERSION = "0.5.5"; + private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index 0d5e649888a4843c1520054d9672f80c62ebbb48..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 100644 GIT binary patch delta 3148 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 delta 3147 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 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 7d59a01f..642d572c 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar +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 diff --git a/mvnw b/mvnw index c5e1a770..a16b5431 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -54,38 +54,16 @@ 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 - ;; + # 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 @@ -130,13 +108,12 @@ if $cygwin ; then CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi -# For Migwn, ensure paths are in UNIX format before anything is touched +# For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -184,27 +161,28 @@ 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) + + 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 - wdir=$(cd "$wdir/.."; pwd) + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround done echo "${basedir}" } @@ -216,9 +194,108 @@ concat_lines() { fi } -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +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 $@" @@ -226,29 +303,8 @@ export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -echo "Running version check" -VERSION=$( sed '\!//' -e 's!.*$!!' ) -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} "$@" + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index b26ab24f..86115719 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Maven Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -26,7 +26,7 @@ @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_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 @@ -120,7 +120,7 @@ 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.5/maven-wrapper-0.5.5.jar" +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 @@ -134,7 +134,7 @@ if exist %WRAPPER_JAR% ( ) ) else ( if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar" + 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 ... From 92cb325837af7ffb5b7c381dedf2fee83dde0aff Mon Sep 17 00:00:00 2001 From: Richard Lyon Date: Fri, 11 Jun 2021 22:12:36 -0400 Subject: [PATCH 58/76] Add app groupId mojo tag to configure the app's alternate group If app#groupId is provided, the value is used as the application maven groupId as well as the base package for the generated packageName in the generator. The app#groupId defaults "org.springframework.cloud.stream.app". Resolves #4 --- .../SpringCloudStreamAppGeneratorMojo.java | 17 +++++++++++++++++ .../app/plugin/generator/AppDefinition.java | 15 ++++++++++++++- .../app/plugin/generator/ProjectGenerator.java | 3 ++- .../src/main/resources/template/app-pom.xml | 2 +- .../resources/template/apps-modules-pom.xml | 2 +- .../dataflow/app/plugin/MojoHarnessTest.java | 2 +- .../resources/unit/http-source-apps/pom.xml | 1 + 7 files changed, 37 insertions(+), 5 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index e5620568..6cbdad8f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -120,6 +120,10 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { 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? @@ -433,6 +437,11 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { */ 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. */ @@ -512,6 +521,14 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { 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(); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index d8ddaeb3..21719db2 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -52,7 +52,12 @@ public class AppDefinition { private String configClass; /** - * Spring Cloud Function defintion. + * Application's group ID. + */ + private String groupId; + + /** + * Spring Cloud Function definition. */ private String functionDefinition; @@ -148,6 +153,14 @@ public class AppDefinition { this.configClass = configClass; } + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + public String getFunctionDefinition() { return functionDefinition; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java index 04d2b644..1ec76743 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/ProjectGenerator.java @@ -128,7 +128,8 @@ public final class ProjectGenerator { capitalize(appDefinition.getType().name()), capitalize(binder.getName())); - String appPackageName = String.format("org.springframework.cloud.stream.app.%s.%s.%s", + String appPackageName = String.format("%s.%s.%s.%s", + appDefinition.getGroupId(), toPkg(appDefinition.getName()), appDefinition.getType(), binder.getName()); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index d83275fb..5b7d5749 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -9,7 +9,7 @@ {{app.springBootVersion}} - org.springframework.cloud.stream.app + {{app.groupId}} {{app.name}}-{{app.type}}-{{app-binder.name}} {{app.version}} {{app.name}}-{{app.type}}-{{app-binder.name}} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml index 793e20a6..cddc638c 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.springframework.cloud.stream.app + {{app.groupId}} {{app.name}}-{{app.type}}-apps {{app.version}} pom diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index abb51609..d8757f10 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -90,7 +90,7 @@ public class MojoHarnessTest { assertThat(parent.getVersion()).isEqualTo("2.3.0.M1"); assertThat(pomModel.getArtifactId()).isEqualTo("http-source-kafka"); - assertThat(pomModel.getGroupId()).isEqualTo("org.springframework.cloud.stream.app"); + 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"); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index d0fbb19d..df6fab58 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -231,6 +231,7 @@ source ${project.version} io.pivotal.java.function.http.supplier.HttpSupplierConfiguration.class + org.springframework.cloud.stream.app.test reactive From a7b78524721ad611a2c6f66f1c9813946e67dbc4 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Fri, 24 Sep 2021 11:19:52 -0400 Subject: [PATCH 59/76] 1.0.4 Release --- pom.xml | 3 +-- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 6210635e..c9cd6872 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4-SNAPSHOT + 1.0.4 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom @@ -287,4 +287,3 @@ - diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index a18876d0..24bc3442 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4-SNAPSHOT + 1.0.4 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index b802fa0c..b66fa53f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4-SNAPSHOT + 1.0.4 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index e33125c1..d8c11c1a 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4-SNAPSHOT + 1.0.4 .. From b2c55e069a0f6ae19b98f166c7f5a935ce82fda5 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Fri, 24 Sep 2021 11:30:23 -0400 Subject: [PATCH 60/76] Next version: 1.0.5-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c9cd6872..854d1b4d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4 + 1.0.5-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 24bc3442..58519b6c 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4 + 1.0.5-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index b66fa53f..c61ab6a1 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4 + 1.0.5-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index d8c11c1a..796ce7cf 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.4 + 1.0.5-SNAPSHOT .. From f68f97bbec5ffabec9d1e2911bcfddae5af8857a Mon Sep 17 00:00:00 2001 From: David Turanski Date: Tue, 5 Oct 2021 14:25:39 -0400 Subject: [PATCH 61/76] Enable actuator discovery --- .../src/main/resources/template/app.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties index 887ffd81..e178532f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties @@ -3,7 +3,7 @@ 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 spring.cloud.function.definition={{app.functionDefinition}} {{#app.consumer}} From 463e8cdc6e72647ec6b2485589b7e45caa622039 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 7 Oct 2021 15:57:52 -0400 Subject: [PATCH 62/76] 1.0.5 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 854d1b4d..e0116d43 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5-SNAPSHOT + 1.0.5 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 58519b6c..203324b0 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5-SNAPSHOT + 1.0.5 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index c61ab6a1..21967e06 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5-SNAPSHOT + 1.0.5 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 796ce7cf..3fb81027 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5-SNAPSHOT + 1.0.5 .. From dd260e44a3aae65a102e80794f0009bb2c3d5b56 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 7 Oct 2021 16:05:04 -0400 Subject: [PATCH 63/76] Next version: 1.0.6-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index e0116d43..70d9fc4d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5 + 1.0.6-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 203324b0..8d26b908 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5 + 1.0.6-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 21967e06..2218566f 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5 + 1.0.6-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 3fb81027..522fc977 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.5 + 1.0.6-SNAPSHOT .. From 41155af3dc30b2aae5ced68836af2fb55ed3f705 Mon Sep 17 00:00:00 2001 From: David Turanski Date: Mon, 24 Jan 2022 11:27:33 -0500 Subject: [PATCH 64/76] Add for boot 2.6 --- .../src/main/resources/template/app.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties index e178532f..84c4041a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app.properties @@ -4,6 +4,7 @@ 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}} From 54f861e1115ec405f64cd7b580093150127b5210 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 27 Jan 2022 16:08:16 -0500 Subject: [PATCH 65/76] Enable custom Spring Boot Maven plugin config Allow user applications to provide custom Spring Boot Maven plugin configuration. The app generator plugin takes these custom configuration elements, which are provided as CDATA, and then add it to the Spring Boot Maven plugin configuration when it generates app's Maven configuration (pom.xml). Adding tests and docs. --- .../README.adoc | 47 +++++++++++++++++++ .../SpringCloudStreamAppGeneratorMojo.java | 15 +++++- .../app/plugin/generator/AppDefinition.java | 13 ++++- .../src/main/resources/template/app-pom.xml | 5 ++ ...SpringCloudStreamAppGeneratorMojoTest.java | 29 +++++++++++- 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/README.adoc b/spring-cloud-dataflow-apps-generator-plugin/README.adoc index 72ea17ad..e7cf3592 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/README.adoc +++ b/spring-cloud-dataflow-apps-generator-plugin/README.adoc @@ -253,3 +253,50 @@ binders and to your binders section configuration like this: ... ---- + +== 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. +``` + + org.springframework.boot + spring-boot-maven-plugin + + + + org.python + jython-standalone + + + + +``` + +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. +``` + + org.springframework.cloud + spring-cloud-dataflow-apps-generator-plugin + + + ... + + + + org.python + jython-standalone + + + ]]> + + ... + + +``` + +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. \ No newline at end of file diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java index 6cbdad8f..ecd80e2d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojo.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * 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. @@ -52,6 +52,7 @@ import org.springframework.util.StringUtils; /** * @author Christian Tzolov * @author David Turanski + * @author Soby Chacko */ @Mojo(name = "generate-app") public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { @@ -305,6 +306,8 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { }) .collect(Collectors.toList()); + app.setBootPluginConfiguration(this.application.getBootPluginConfiguration()); + // ---------------------------------------------------------------------------------------------------------- // Project Generator // ---------------------------------------------------------------------------------------------------------- @@ -461,6 +464,16 @@ public class SpringCloudStreamAppGeneratorMojo extends AbstractMojo { private final Maven maven = new Maven(); + private String bootPluginConfiguration; + + public String getBootPluginConfiguration() { + return bootPluginConfiguration; + } + + public void setBootPluginConfiguration(String bootPluginConfiguration) { + this.bootPluginConfiguration = bootPluginConfiguration; + } + public Map getProperties() { return properties; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 21719db2..8ea5249b 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * 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. @@ -23,6 +23,7 @@ import java.util.List; * Application configurations used to parameterize the project templates. * * @author Christian Tzolov + * @author Soby Chacko */ public class AppDefinition { @@ -81,6 +82,16 @@ public class AppDefinition { */ 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; } diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index 5b7d5749..e8e3b846 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -134,6 +134,11 @@ org.springframework.boot spring-boot-maven-plugin + {{#app.bootPluginConfiguration}} + + {{this}} + + {{/app.bootPluginConfiguration}} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java index 9f776f25..e06347c9 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/SpringCloudStreamAppGeneratorMojoTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * 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. @@ -24,12 +24,14 @@ 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; @@ -44,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Christian Tzolov + * @author Soby Chacko */ public class SpringCloudStreamAppGeneratorMojoTest { @@ -146,6 +149,30 @@ public class SpringCloudStreamAppGeneratorMojoTest { assertGeneratedPomXml(new File(projectHome.getRoot().getAbsolutePath())); } + @Test + public void testCustomBootMavenPluginConfiguration() throws Exception { + application.setBootPluginConfiguration("\n" + + " \n" + + " org.python\n" + + " jython-standalone\n" + + " \n" + + " \n" + + " ]]>"); + + springCloudStreamAppMojo.execute(); + + Model pomModel = getModel(new File(projectHome.getRoot().getAbsolutePath())); + List plugins = pomModel.getBuild().getPlugins(); + final Optional 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("")).isTrue(); + assertThat(configuration.getValue().contains("jython-standalone")).isTrue(); + assertThat(configuration.getValue().contains("")).isTrue(); + } + private void assertGeneratedPomXml(File rootPath) { Model pomModel = getModel(rootPath); From 977d37d25ccfebd9420227b3ec2d17e285f11fc0 Mon Sep 17 00:00:00 2001 From: onobc Date: Wed, 2 Feb 2022 16:37:06 -0600 Subject: [PATCH 66/76] Remove 'latest' tag from jib docker image --- .../src/main/resources/template/app-pom.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index e8e3b846..e2c528eb 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -150,10 +150,7 @@ {{app.containerImage.baseImage}} - {{app.containerImage.orgName}}/${project.artifactId} - - {{app.containerImage.tag}} - + {{app.containerImage.orgName}}/${project.artifactId}:${{app.containerImage.tag}} USE_CURRENT_TIMESTAMP From 1c823619c8efe86590977c472c8e7046d8c4c1b7 Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Wed, 2 Feb 2022 19:13:30 -0600 Subject: [PATCH 67/76] Fix tests broken from previous commit (#13) --- .../src/main/resources/template/app-pom.xml | 2 +- .../cloud/dataflow/app/plugin/MojoHarnessTest.java | 3 +-- .../src/test/resources/unit/http-source-apps/pom.xml | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index e2c528eb..c01d2ffd 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -150,7 +150,7 @@ {{app.containerImage.baseImage}} - {{app.containerImage.orgName}}/${project.artifactId}:${{app.containerImage.tag}} + {{app.containerImage.orgName}}/${project.artifactId}:{{app.containerImage.tag}} USE_CURRENT_TIMESTAMP diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java index d8757f10..2c49ef66 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/java/org/springframework/cloud/dataflow/app/plugin/MojoHarnessTest.java @@ -106,8 +106,7 @@ public class MojoHarnessTest { .contains("" + "${org.springframework.cloud.dataflow.spring.configuration.metadata.json}" + ""); - assertThat(jibPlugin.getConfiguration().toString()).contains("testspringcloud/${project.artifactId}"); - assertThat(jibPlugin.getConfiguration().toString()).contains("3.0.0.BUILD-SNAPSHOT"); + assertThat(jibPlugin.getConfiguration().toString()).contains("testspringcloud/${project.artifactId}:3.0.0.BUILD-SNAPSHOT"); assertThat(jibPlugin.getConfiguration().toString()).contains("globalBaseImage"); assertThat(pomModel.getRepositories().size()).isEqualTo(5); diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index df6fab58..69fd616d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -142,10 +142,7 @@ springcloud/baseimage:1.0.0 - springcloudstream:${project.artifactId} - - latest - + springcloudstream:${project.artifactId}:3.0.0.BUILD-SNAPSHOT USE_CURRENT_TIMESTAMP From a12ccd9b8205700fa2437766ea591f26e3f2fa9c Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 24 Feb 2022 10:57:55 -0500 Subject: [PATCH 68/76] 1.0.6 Release --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 70d9fc4d..40b7417c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6-SNAPSHOT + 1.0.6 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 8d26b908..4680d265 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6-SNAPSHOT + 1.0.6 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index 2218566f..eead6936 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6-SNAPSHOT + 1.0.6 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 522fc977..c0357851 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6-SNAPSHOT + 1.0.6 .. From 94c3743d0c3cc3a66d84c15e6530e27428de5d5a Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Thu, 24 Feb 2022 11:10:40 -0500 Subject: [PATCH 69/76] Next snapshot: 1.0.7-SNAPSHOT --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 40b7417c..ff4ec985 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6 + 1.0.7-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index 4680d265..dbe0554e 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6 + 1.0.7-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index eead6936..fb686c6d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6 + 1.0.7-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index c0357851..b17ea28f 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.6 + 1.0.7-SNAPSHOT .. From e677e2bc1b0f1d0fdaa29c1bc5334fda868ef0bf Mon Sep 17 00:00:00 2001 From: Corneil du Plessis Date: Tue, 3 May 2022 21:26:42 +0200 Subject: [PATCH 70/76] ensure baseimage version is in property that should be provided by stream-applications-core --- .../src/test/resources/unit/http-source-apps/pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index 69fd616d..d951668a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -21,6 +21,7 @@ Hoxton.RELEASE Horsham.SR2 3.0.2.RELEASE + springcloud/baseimage:1.0.0 @@ -139,7 +140,7 @@ - springcloud/baseimage:1.0.0 + ${apps.base-image} springcloudstream:${project.artifactId}:3.0.0.BUILD-SNAPSHOT From bae7f10a5f942252a63c8dd9dbaa0fde7ac8260a Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Tue, 31 May 2022 22:39:13 -0500 Subject: [PATCH 71/76] 1.0.7 Release (#18) --- pom.xml | 2 +- spring-cloud-dataflow-apps-docs-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-generator-plugin/pom.xml | 2 +- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index ff4ec985..cfada87b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7-SNAPSHOT + 1.0.7 spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index dbe0554e..f6bf896a 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7-SNAPSHOT + 1.0.7 .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index fb686c6d..dbbc42b9 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7-SNAPSHOT + 1.0.7 .. diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index b17ea28f..d051f544 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7-SNAPSHOT + 1.0.7 .. From 4e8f802439426dc50831e4afd08b110739501a19 Mon Sep 17 00:00:00 2001 From: Corneil du Plessis Date: Wed, 19 Oct 2022 16:52:12 +0200 Subject: [PATCH 72/76] update baseImage and jib-maven-plugin (#19) * ensure baseimage version is in property that should be provided by stream-applications-core * Updating baseImage to 1.0.4 and jib-maven-plugin to 3.3.0 --- .../cloud/dataflow/app/plugin/MavenXmlWriter.java | 4 ++-- .../cloud/dataflow/app/plugin/generator/AppDefinition.java | 2 +- .../src/main/resources/template/app-pom.xml | 2 +- .../src/main/resources/template/apps-modules-pom.xml | 2 +- .../src/test/resources/unit/http-source-apps/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java index 74c424cb..8294dde2 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/MavenXmlWriter.java @@ -71,10 +71,10 @@ public final class MavenXmlWriter { * * com.google.cloud.tools * jib-maven-plugin - * 2.6.0 + * 3.3.0 * * springcloud/baseimage:1.0.0 + * springcloud/baseimage:1.0.4 * * springcloudstream:${project.artifactId} * latest diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index 8ea5249b..a2ae2672 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -222,7 +222,7 @@ public class AppDefinition { private String tag = "latest"; - private String baseImage = "springcloud/baseimage:1.0.0"; + private String baseImage = "springcloud/baseimage:1.0.4"; public ContainerImageFormat getFormat() { return format; diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index c01d2ffd..eb81984c 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -144,7 +144,7 @@ com.google.cloud.tools jib-maven-plugin - 2.6.0 + 3.3.0 {{app.containerImage.baseImage}} diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml index cddc638c..aba173f8 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/apps-modules-pom.xml @@ -83,7 +83,7 @@ com.google.cloud.tools jib-maven-plugin - 2.6.0 + 3.3.0 diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml index d951668a..d65f0904 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/test/resources/unit/http-source-apps/pom.xml @@ -21,7 +21,7 @@ Hoxton.RELEASE Horsham.SR2 3.0.2.RELEASE - springcloud/baseimage:1.0.0 + springcloud/baseimage:1.0.4
From b797c797ad35dda468614e5a97ec4c322016c7c6 Mon Sep 17 00:00:00 2001 From: Corneil du Plessis Date: Wed, 25 Jan 2023 15:41:59 +0200 Subject: [PATCH 73/76] Fix repo.spring.io references. (#21) Fixes #20 --- pom.xml | 10 +++---- .../pom.xml | 8 ------ .../src/main/resources/template/app-pom.xml | 4 +-- .../pom.xml | 28 ++----------------- 4 files changed, 9 insertions(+), 41 deletions(-) diff --git a/pom.xml b/pom.xml index cfada87b..00c5fabb 100644 --- a/pom.xml +++ b/pom.xml @@ -202,12 +202,12 @@ repo.spring.io Spring Release Repository - https://repo.spring.io/release + https://repo.spring.io/libs-release-local repo.spring.io Spring Snapshot Repository - https://repo.spring.io/snapshot + https://repo.spring.io/libs-snapshot-local @@ -229,12 +229,12 @@ spring-milestones Spring Milestones - https://repo.spring.io/milestone + https://repo.spring.io/plugins-milestone spring-snapshots Spring Snapshots - https://repo.spring.io/snapshot + https://repo.spring.io/plugins-snapshot true @@ -249,7 +249,7 @@ repo.spring.io Spring Milestone Repository - https://repo.spring.io/milestone + https://repo.spring.io/libs-milestone-local diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index f6bf896a..fc180bc8 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -88,14 +88,6 @@ Spring Milestones https://repo.spring.io/milestone - - - false - - spring-releases - Spring Releases - https://repo.spring.io/release - diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index eb81984c..f649806a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -109,7 +109,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/snapshot + https://repo.spring.io/plugins-snapshot @@ -117,7 +117,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/milestone + https://repo.spring.io/plugins-milestone diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index d051f544..363c14ca 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -126,30 +126,6 @@ Spring Milestones https://repo.spring.io/milestone - - - false - - spring-releases - Spring Releases - https://repo.spring.io/release - - - - false - - spring-libs-release - Spring Libs Release - https://repo.spring.io/libs-release - - - - false - - spring-milestone-release - Spring Milestone Release - https://repo.spring.io/milestone - @@ -158,7 +134,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/snapshot + https://repo.spring.io/plugins-snapshot @@ -166,7 +142,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/milestone + https://repo.spring.io/plugins-milestone From b9e9c6a54265bc9556de48e84722474edcaee143 Mon Sep 17 00:00:00 2001 From: Corneil du Plessis Date: Thu, 26 Jan 2023 19:06:28 +0200 Subject: [PATCH 74/76] Replace repo.spring.io/plugins- references. (#22) Fixes #20 --- pom.xml | 4 ++-- .../src/main/resources/template/app-pom.xml | 4 ++-- spring-cloud-dataflow-apps-metadata-plugin/pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 00c5fabb..2469fc6b 100644 --- a/pom.xml +++ b/pom.xml @@ -229,12 +229,12 @@ spring-milestones Spring Milestones - https://repo.spring.io/plugins-milestone + https://repo.spring.io/milestone spring-snapshots Spring Snapshots - https://repo.spring.io/plugins-snapshot + https://repo.spring.io/snapshot true diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml index f649806a..eb81984c 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/app-pom.xml @@ -109,7 +109,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/plugins-snapshot + https://repo.spring.io/snapshot @@ -117,7 +117,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/plugins-milestone + https://repo.spring.io/milestone diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 363c14ca..4b24a516 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -134,7 +134,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/plugins-snapshot + https://repo.spring.io/snapshot @@ -142,7 +142,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/plugins-milestone + https://repo.spring.io/milestone From c888f45a1d52f9403c0d9108a7077cfc063fa9f5 Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Mon, 5 Jun 2023 12:16:32 +0200 Subject: [PATCH 75/76] Add auto-config support - Add a marker value for the 'configClass' attriubte: 'AUTOCONFIGURATION'. - If the configClass=='AUTOCONFIGURATION' then the '@Import(configClass)' statment is dropped from the generated app. - Update maven versions to 3.9.x - Udate project versions from 1.0.7 to 1.0.8-SNAPSHOT Resolves #23 --- .gitignore | 2 ++ pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 19 ++++++++++++++----- .../app/plugin/generator/AppDefinition.java | 13 +++++++++++-- .../src/main/resources/template/App.java | 2 ++ .../pom.xml | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index f5712a10..d9338952 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ dump.rdb coverage-error.log .apt_generated aws.credentials.properties +.vscode/ + diff --git a/pom.xml b/pom.xml index 2469fc6b..77769964 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7 + 1.0.8-SNAPSHOT spring-cloud-dataflow-apps-plugin-parent Spring Cloud Dataflow Apps Plugin Parent pom diff --git a/spring-cloud-dataflow-apps-docs-plugin/pom.xml b/spring-cloud-dataflow-apps-docs-plugin/pom.xml index fc180bc8..b01b48f9 100644 --- a/spring-cloud-dataflow-apps-docs-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-docs-plugin/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7 + 1.0.8-SNAPSHOT .. diff --git a/spring-cloud-dataflow-apps-generator-plugin/pom.xml b/spring-cloud-dataflow-apps-generator-plugin/pom.xml index dbbc42b9..9c0b289e 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-generator-plugin/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 spring-cloud-dataflow-apps-generator-plugin @@ -9,16 +10,17 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7 + 1.0.8-SNAPSHOT .. UTF-8 - 3.6.3 - 3.6.0 + 3.9.2 + 3.9.0 1.15 - 5.2.8.RELEASE + 6.0.8 + 2.12.0 @@ -52,6 +54,13 @@ provided
+ + commons-io + commons-io + ${commons.io.version} + + + junit junit diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java index a2ae2672..ae33ec9d 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/java/org/springframework/cloud/dataflow/app/plugin/generator/AppDefinition.java @@ -27,6 +27,12 @@ import java.util.List; */ 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. */ @@ -156,6 +162,10 @@ public class AppDefinition { return type == AppType.processor; } + public boolean isAutoconfiguration() { + return AUTOCONFIGURATION_MARKER.equalsIgnoreCase(configClass.trim()); + } + public String getConfigClass() { return configClass; } @@ -213,8 +223,7 @@ public class AppDefinition { private ContainerImageFormat format = ContainerImageFormat.Docker; /** - * True will attempt to inline the (white) filtered Spring Boot metadata as Base64 encoded - * property. + * True will attempt to inline the (white) filtered Spring Boot metadata as Base64 encoded property. */ private boolean enableMetadata = false; diff --git a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/App.java b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/App.java index 2a3d1bfb..efe8698a 100644 --- a/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/App.java +++ b/spring-cloud-dataflow-apps-generator-plugin/src/main/resources/template/App.java @@ -22,7 +22,9 @@ 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) { diff --git a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml index 4b24a516..d566b116 100644 --- a/spring-cloud-dataflow-apps-metadata-plugin/pom.xml +++ b/spring-cloud-dataflow-apps-metadata-plugin/pom.xml @@ -8,7 +8,7 @@ org.springframework.cloud spring-cloud-dataflow-apps-plugin-parent - 1.0.7 + 1.0.8-SNAPSHOT .. From e2c1c82d1d618e419f4168821fc23d974dac31fe Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Mon, 5 Jun 2023 16:34:07 -0400 Subject: [PATCH 76/76] Update java.version to 17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 77769964..a4a0084b 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ pom - 1.8 + 17 3.1.1 3.2.1 2.22.2