http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/components/camel-zookeeper/src/main/resources/META-INF/spring.factories ---------------------------------------------------------------------- diff --git a/components/camel-zookeeper/src/main/resources/META-INF/spring.factories b/components/camel-zookeeper/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 998f9e1..0000000 --- a/components/camel-zookeeper/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,19 +0,0 @@ -# -# 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 -# -# 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. -# - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.apache.camel.component.zookeeper.springboot.ZooKeeperComponentAutoConfiguration
http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/components/pom.xml ---------------------------------------------------------------------- diff --git a/components/pom.xml b/components/pom.xml index a1b023d..eea5d75 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -311,6 +311,7 @@ <id>validate</id> <goals> <goal>validate-components</goal> + <goal>prepare-spring-boot-starter</goal> <goal>prepare-spring-boot-auto-configuration</goal> </goals> <phase>prepare-package</phase> http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index a706be7..4de41f9 100755 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,7 @@ <module>platforms</module> <module>tests</module> <module>examples</module> + <module>components-starter</module> </modules> <scm> http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/pom.xml b/tooling/maven/camel-package-maven-plugin/pom.xml index db2b421..5acff48 100644 --- a/tooling/maven/camel-package-maven-plugin/pom.xml +++ b/tooling/maven/camel-package-maven-plugin/pom.xml @@ -110,23 +110,22 @@ <version>${spring-boot-version}</version> </dependency> - <!-- logging --> + <!-- Freemarker for xml templating --> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-api</artifactId> - <scope>test</scope> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>${freemarker-version}</version> </dependency> + + <!-- logging --> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-core</artifactId> - <scope>test</scope> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> </dependency> <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-slf4j-impl</artifactId> - <scope>test</scope> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> </dependency> - </dependencies> </project> http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java index 27b49b6..1c265a2 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootAutoConfigurationMojo.java @@ -68,6 +68,8 @@ import static org.apache.camel.maven.packaging.PackageHelper.loadText; */ public class SpringBootAutoConfigurationMojo extends AbstractMojo { + private static final boolean DELETE_FILES_ON_MAIN_ARTIFACTS = true; + /** * The maven project. * @@ -85,18 +87,25 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { protected File buildDir; /** + * The base directory + * + * @parameter default-value="${basedir}" + */ + protected File baseDir; + + /** * The source directory * * @parameter default-value="${basedir}/src/main/java" */ - protected File srcDir; + protected File srcDir2222; /** * The resources directory * * @parameter default-value="${basedir}/src/main/resources" */ - protected File resourcesDir; + protected File resourcesDir2222; /** * build context to check changed files and mark them for refresh (used for @@ -334,41 +343,19 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } // CHECKSTYLE:OFF private static boolean skipComponentOption(ComponentModel model, ComponentOptionModel option) { if ("netty4-http".equals(model.getScheme()) || "netty-http".equals(model.getScheme())) { String name = option.getName(); - if (name.equals("textline") || name.equals("delimiter") || name.equals("autoAppendDelimiter") || name.equals("decoderMaxLineLength") - || name.equals("encoding") || name.equals("allowDefaultCodec") || name.equals("udpConnectionlessSending") || name.equals("networkInterface") - || name.equals("clientMode") || name.equals("reconnect") || name.equals("reconnectInterval") || name.equals("useByteBuf") - || name.equals("udpByteArrayCodec") || name.equals("broadcast")) { - return true; + if (name.equals("textline") || name.equals("delimiter") || name.equals("autoAppendDelimiter") || name.equals("decoderMaxLineLength") + || name.equals("encoding") || name.equals("allowDefaultCodec") || name.equals("udpConnectionlessSending") || name.equals("networkInterface") + || name.equals("clientMode") || name.equals("reconnect") || name.equals("reconnectInterval") || name.equals("useByteBuf") + || name.equals("udpByteArrayCodec") || name.equals("broadcast")) { + return true; } } return false; @@ -444,30 +431,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } private void createLanguageConfigurationSource(String packageName, LanguageModel model, String overrideLanguageName) throws MojoFailureException { @@ -570,30 +535,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } private void createComponentAutoConfigurationSource(String packageName, ComponentModel model, List<String> componentAliases) throws MojoFailureException { @@ -644,30 +587,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } private void createDataFormatAutoConfigurationSource(String packageName, DataFormatModel model, List<String> dataFormatAliases) throws MojoFailureException { @@ -721,30 +642,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } private void createLanguageAutoConfigurationSource(String packageName, LanguageModel model, List<String> languageAliases) throws MojoFailureException { @@ -798,30 +697,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { sortImports(javaClass); String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java"; - File target = new File(srcDir, fileName); - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); - String header = loadText(is); - String code = sourceToString(javaClass); - code = header + code; - getLog().debug("Source code generated:\n" + code); - - if (target.exists()) { - String existing = FileUtils.readFileToString(target); - if (!code.equals(existing)) { - FileUtils.write(target, code, false); - getLog().info("Updated existing file: " + target); - } else { - getLog().debug("No changes to existing file: " + target); - } - } else { - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } + writeSourceIfChanged(javaClass, fileName); } private void createComponentSpringFactorySource(String packageName, ComponentModel model) throws MojoFailureException { @@ -831,69 +708,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { int pos = model.getJavaType().lastIndexOf("."); String name = model.getJavaType().substring(pos + 1); name = name.replace("Component", "ComponentAutoConfiguration"); - String lineToAdd = packageName + "." + name + "\n"; - sb.append(lineToAdd); - - String fileName = "META-INF/spring.factories"; - File target = new File(resourcesDir, fileName); - - if (target.exists()) { - try { - // is the auto configuration already in the file - boolean found = false; - List<String> lines = FileUtils.readLines(target); - for (String line : lines) { - if (line.contains(name)) { - found = true; - break; - } - } - - if (found) { - getLog().debug("No changes to existing file: " + target); - } else { - // find last non empty line, so we can add our new line after that - int lastLine = 0; - for (int i = lines.size() - 1; i >= 0; i--) { - String line = lines.get(i); - if (!line.trim().isEmpty()) { - // adjust existing line so its being continued - line = line + ",\\"; - lines.set(i, line); - lastLine = i; - break; - } - } - lines.add(lastLine + 1, lineToAdd); - - StringBuilder code = new StringBuilder(); - for (String line : lines) { - code.append(line).append("\n"); - } - - // update - FileUtils.write(target, code.toString(), false); - getLog().info("Updated existing file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } else { - // create new file - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header.txt"); - String header = loadText(is); - String code = sb.toString(); - // add empty new line after header - code = header + "\n" + code; - getLog().debug("Source code generated:\n" + code); - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } + writeComponentSpringFactorySource(packageName, name); } private void createDataFormatSpringFactorySource(String packageName, DataFormatModel model) throws MojoFailureException { @@ -903,69 +719,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { int pos = model.getJavaType().lastIndexOf("."); String name = model.getJavaType().substring(pos + 1); name = name.replace("DataFormat", "DataFormatAutoConfiguration"); - String lineToAdd = packageName + "." + name + "\n"; - sb.append(lineToAdd); - - String fileName = "META-INF/spring.factories"; - File target = new File(resourcesDir, fileName); - - if (target.exists()) { - try { - // is the auto configuration already in the file - boolean found = false; - List<String> lines = FileUtils.readLines(target); - for (String line : lines) { - if (line.contains(name)) { - found = true; - break; - } - } - if (found) { - getLog().debug("No changes to existing file: " + target); - } else { - // find last non empty line, so we can add our new line after that - int lastLine = 0; - for (int i = lines.size() - 1; i >= 0; i--) { - String line = lines.get(i); - if (!line.trim().isEmpty()) { - // adjust existing line so its being continued - line = line + ",\\"; - lines.set(i, line); - lastLine = i; - break; - } - } - lines.add(lastLine + 1, lineToAdd); - - StringBuilder code = new StringBuilder(); - for (String line : lines) { - code.append(line).append("\n"); - } - - // update - FileUtils.write(target, code.toString(), false); - getLog().info("Updated existing file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } else { - // create new file - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header.txt"); - String header = loadText(is); - String code = sb.toString(); - // add empty new line after header - code = header + "\n" + code; - getLog().debug("Source code generated:\n" + code); - - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } + writeComponentSpringFactorySource(packageName, name); } private void createLanguageSpringFactorySource(String packageName, LanguageModel model) throws MojoFailureException { @@ -975,69 +730,8 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { int pos = model.getJavaType().lastIndexOf("."); String name = model.getJavaType().substring(pos + 1); name = name.replace("Language", "LanguageAutoConfiguration"); - String lineToAdd = packageName + "." + name + "\n"; - sb.append(lineToAdd); - - String fileName = "META-INF/spring.factories"; - File target = new File(resourcesDir, fileName); - - if (target.exists()) { - try { - // is the auto configuration already in the file - boolean found = false; - List<String> lines = FileUtils.readLines(target); - for (String line : lines) { - if (line.contains(name)) { - found = true; - break; - } - } - - if (found) { - getLog().debug("No changes to existing file: " + target); - } else { - // find last non empty line, so we can add our new line after that - int lastLine = 0; - for (int i = lines.size() - 1; i >= 0; i--) { - String line = lines.get(i); - if (!line.trim().isEmpty()) { - // adjust existing line so its being continued - line = line + ",\\"; - lines.set(i, line); - lastLine = i; - break; - } - } - lines.add(lastLine + 1, lineToAdd); - StringBuilder code = new StringBuilder(); - for (String line : lines) { - code.append(line).append("\n"); - } - - // update - FileUtils.write(target, code.toString(), false); - getLog().info("Updated existing file: " + target); - } - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } else { - // create new file - try { - InputStream is = getClass().getClassLoader().getResourceAsStream("license-header.txt"); - String header = loadText(is); - String code = sb.toString(); - // add empty new line after header - code = header + "\n" + code; - getLog().debug("Source code generated:\n" + code); - - FileUtils.write(target, code); - getLog().info("Created file: " + target); - } catch (Exception e) { - throw new MojoFailureException("IOError with file " + target, e); - } - } + writeComponentSpringFactorySource(packageName, name); } private static String createComponentBody(String shortJavaType) { @@ -1391,4 +1085,121 @@ public class SpringBootAutoConfigurationMojo extends AbstractMojo { return languageNames; } + private void writeSourceIfChanged(JavaClassSource source, String fileName) throws MojoFailureException { + + File target = new File(SpringBootHelper.starterSrcDir(baseDir), fileName); + + deleteFileOnMainArtifact(target); + + try { + InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt"); + String header = loadText(is); + String code = sourceToString(source); + code = header + code; + getLog().debug("Source code generated:\n" + code); + + if (target.exists()) { + String existing = FileUtils.readFileToString(target); + if (!code.equals(existing)) { + FileUtils.write(target, code, false); + getLog().info("Updated existing file: " + target); + } else { + getLog().debug("No changes to existing file: " + target); + } + } else { + FileUtils.write(target, code); + getLog().info("Created file: " + target); + } + } catch (Exception e) { + throw new MojoFailureException("IOError with file " + target, e); + } + } + + private void writeComponentSpringFactorySource(String packageName, String name) throws MojoFailureException { + StringBuilder sb = new StringBuilder(); + sb.append("org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\n"); + + name = name.replace("Component", "ComponentAutoConfiguration"); + String lineToAdd = packageName + "." + name + "\n"; + sb.append(lineToAdd); + + String fileName = "META-INF/spring.factories"; + File target = new File(SpringBootHelper.starterResourceDir(baseDir), fileName); + + deleteFileOnMainArtifact(target); + + if (target.exists()) { + try { + // is the auto configuration already in the file + boolean found = false; + List<String> lines = FileUtils.readLines(target); + for (String line : lines) { + if (line.contains(name)) { + found = true; + break; + } + } + + if (found) { + getLog().debug("No changes to existing file: " + target); + } else { + // find last non empty line, so we can add our new line after that + int lastLine = 0; + for (int i = lines.size() - 1; i >= 0; i--) { + String line = lines.get(i); + if (!line.trim().isEmpty()) { + // adjust existing line so its being continued + line = line + ",\\"; + lines.set(i, line); + lastLine = i; + break; + } + } + lines.add(lastLine + 1, lineToAdd); + + StringBuilder code = new StringBuilder(); + for (String line : lines) { + code.append(line).append("\n"); + } + + // update + FileUtils.write(target, code.toString(), false); + getLog().info("Updated existing file: " + target); + } + } catch (Exception e) { + throw new MojoFailureException("IOError with file " + target, e); + } + } else { + // create new file + try { + InputStream is = getClass().getClassLoader().getResourceAsStream("license-header.txt"); + String header = loadText(is); + String code = sb.toString(); + // add empty new line after header + code = header + "\n" + code; + getLog().debug("Source code generated:\n" + code); + + FileUtils.write(target, code); + getLog().info("Created file: " + target); + } catch (Exception e) { + throw new MojoFailureException("IOError with file " + target, e); + } + } + } + + private void deleteFileOnMainArtifact(File starterFile) { + if (!DELETE_FILES_ON_MAIN_ARTIFACTS) { + return; + } + + String relativePath = SpringBootHelper.starterDir(baseDir).toPath().relativize(starterFile.toPath()).toString(); + File mainArtifactFile = new File(baseDir, relativePath); + if (mainArtifactFile.exists()) { + boolean deleted = mainArtifactFile.delete(); + if (!deleted) { + throw new IllegalStateException("Cannot delete file " + mainArtifactFile); + } + } + } + } http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootHelper.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootHelper.java new file mode 100644 index 0000000..14f51fa --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootHelper.java @@ -0,0 +1,70 @@ +/** + * 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 + * + * 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.apache.camel.maven.packaging; + +import java.io.File; +import java.io.IOException; + +/** + * Useful methods for spring-boot mojos. + */ +public final class SpringBootHelper { + + public static final String STARTER_SUFFIX = "-starter"; + + private SpringBootHelper() { + } + + public static File starterSrcDir(File baseDir) { + return new File(starterDir(baseDir), "src/main/java"); + } + + public static File starterResourceDir(File baseDir) { + return new File(starterDir(baseDir), "src/main/resources"); + } + + public static File starterDir(File baseDir) { + String starterName = baseDir.getName() + STARTER_SUFFIX; + + File allStartersDir = allStartersDir(baseDir); + File starterDir = new File(allStartersDir, starterName); + return starterDir; + } + + public static File allStartersDir(File baseDir) { + File allStartersDir = new File(camelProjectRoot(baseDir), "components-starter"); + return allStartersDir; + } + + public static File camelProjectRoot(File baseDir) { + try { + File root = baseDir.getCanonicalFile(); + while (root != null && !root.getName().equals("camel")) { + root = root.getParentFile(); + } + + if (root == null) { + throw new IllegalStateException("Cannot find project root"); + } + return root; + } catch (IOException e) { + throw new IllegalStateException("Error while getting directory", e); + } + } + + +} http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java new file mode 100644 index 0000000..a359c32 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java @@ -0,0 +1,462 @@ +/** + * 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 + * + * 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.apache.camel.maven.packaging; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.sun.org.apache.xml.internal.serialize.OutputFormat; +import com.sun.org.apache.xml.internal.serialize.XMLSerializer; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.artifact.resolver.ArtifactCollector; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.dependency.tree.DependencyNode; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; +import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor; +import org.sonatype.plexus.build.incremental.BuildContext; + +import freemarker.cache.URLTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; + +/** + * Generate Spring Boot starter for the component + * + * @goal prepare-spring-boot-starter + */ +public class SpringBootStarterMojo extends AbstractMojo { + + // TO ADD?: "camel-chronicle", "camel-guava-eventbus" ?, "camel-johnzon", "camel-ribbon" + private static final String[] IGNORE_MODULES = {/* OSGi -> */ "camel-core-osgi", "camel-eventadmin", "camel-paxlogging", /* deprecated -> */"camel-swagger", "camel-mina", /* others -> */ + "camel-spring-boot", "camel-spring-boot-starter", "camel-zipkin", "camel-zipkin-starter"}; + + private static final boolean IGNORE_TEST_MODULES = true; + + /** + * The maven project. + * + * @parameter property="project" + * @required + * @readonly + */ + protected MavenProject project; + + + /** + * The project directory + * + * @parameter default-value="${basedir}" + */ + protected File baseDir; + + /** + * build context to check changed files and mark them for refresh (used for + * m2e compatibility) + * + * @component + * @readonly + */ + private BuildContext buildContext; + + /** + * @component + * @required + * @readonly + */ + private ArtifactFactory artifactFactory; + + /** + * @component + * @required + * @readonly + */ + private ArtifactMetadataSource artifactMetadataSource; + + /** + * @component + * @required + * @readonly + */ + private ArtifactCollector artifactCollector; + + /** + * @component + * @required + * @readonly + */ + private DependencyTreeBuilder treeBuilder; + + /** + * @parameter default-value="${localRepository}" + * @readonly + * @required + */ + protected ArtifactRepository localRepository; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + if (!isStarterAllowed()) { + getLog().info("Spring-Boot-Starter: starter not allowed for module " + project.getArtifactId() + ": skipping."); + return; + } + + try { + // create the starter directory + File starterDir = starterDir(); + getLog().info("Spring-Boot-Starter: starter dir for the component is: " + starterDir.getAbsolutePath()); + + if (!starterDir.exists()) { + starterDir.mkdirs(); + } + + // create the base pom.xml + Document pom = createBasePom(); + + // Apply changes to the starter pom + fixLoggingDependencies(pom); + includeAdditionalDependencies(pom); + + // Write the starter pom + File pomFile = new File(starterDir, "pom.xml"); + writeXmlFormatted(pom, pomFile); + + // write the spring.provides file + writeSpringProvides(); + + // synchronized all starters with their parent pom 'modules' section + synchronizeParentPom(); + + } catch (Exception e) { + throw new MojoFailureException("Unable to create starter", e); + } + + } + + private File starterDir() throws IOException { + return SpringBootHelper.starterDir(baseDir); + } + + private File allStartersDir() throws IOException { + return SpringBootHelper.allStartersDir(baseDir); + } + + private void includeAdditionalDependencies(Document pom) throws Exception { + + Properties properties = new Properties(); + properties.load(getClass().getResourceAsStream("/spring-boot-additional-dependencies.properties")); + + String deps = properties.getProperty(project.getArtifactId()); + if (deps != null && deps.trim().length() > 0) { + getLog().info("Spring-Boot-Starter: the following dependencies will be added to the starter: " + deps); + + XPath xpath = XPathFactory.newInstance().newXPath(); + Node dependencies = ((NodeList) xpath.compile("/project/dependencies").evaluate(pom, XPathConstants.NODESET)).item(0); + + for (String dep : deps.split(",")) { + Element dependency = pom.createElement("dependency"); + dependencies.appendChild(dependency); + + String[] comps = dep.split("\\:"); + String groupIdStr = comps[0]; + String artifactIdStr = comps[1]; + String versionStr = comps.length > 2 ? comps[2] : null; + + Element groupId = pom.createElement("groupId"); + groupId.setTextContent(groupIdStr); + dependency.appendChild(groupId); + + Element artifactId = pom.createElement("artifactId"); + artifactId.setTextContent(artifactIdStr); + dependency.appendChild(artifactId); + + if (versionStr != null) { + Element version = pom.createElement("version"); + version.setTextContent(versionStr); + dependency.appendChild(version); + } + + } + + } + + } + + private void fixLoggingDependencies(Document pom) throws Exception { + + Set<String> loggingImpl = new HashSet<>(); + + loggingImpl.add("commons-logging:commons-logging"); + + loggingImpl.add("log4j:log4j"); + loggingImpl.add("log4j:apache-log4j-extras"); + + loggingImpl.add("org.apache.logging.log4j:log4j"); + + loggingImpl.add("org.slf4j:slf4j-jcl"); + loggingImpl.add("org.slf4j:slf4j-jdk14"); + loggingImpl.add("org.slf4j:slf4j-log4j12"); + loggingImpl.add("org.slf4j:slf4j-log4j13"); + loggingImpl.add("org.slf4j:slf4j-nop"); + loggingImpl.add("org.slf4j:slf4j-simple"); + + + Set<String> includedLibs = filterIncludedArtifacts(loggingImpl); + + if (includedLibs.size() > 0) { + getLog().info("Spring-Boot-Starter: the following dependencies will be removed from the starter: " + includedLibs); + + XPath xpath = XPathFactory.newInstance().newXPath(); + Node dependency = ((NodeList) xpath.compile("/project/dependencies/dependency[artifactId/text() = '" + project.getArtifactId() + "']").evaluate(pom, XPathConstants.NODESET)).item(0); + + Element exclusions = pom.createElement("exclusions"); + + dependency.appendChild(exclusions); + + for (String lib : includedLibs) { + String groupIdStr = lib.split("\\:")[0]; + String artifactIdStr = lib.split("\\:")[1]; + + Element exclusion = pom.createElement("exclusion"); + + Element groupId = pom.createElement("groupId"); + groupId.setTextContent(groupIdStr); + exclusion.appendChild(groupId); + + Element artifactId = pom.createElement("artifactId"); + artifactId.setTextContent(artifactIdStr); + exclusion.appendChild(artifactId); + + exclusions.appendChild(exclusion); + } + } + + } + + private Set<String> filterIncludedArtifacts(Set<String> artifacts) throws DependencyTreeBuilderException { + Set<String> included = new TreeSet<>(); + + ArtifactFilter artifactFilter = new ScopeArtifactFilter(null); + + DependencyNode node = treeBuilder.buildDependencyTree(project, localRepository, artifactFactory, artifactMetadataSource, artifactFilter, artifactCollector); + + CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); + + node.accept(visitor); + + List<DependencyNode> nodes = visitor.getNodes(); + for (DependencyNode dependencyNode : nodes) { + int state = dependencyNode.getState(); + Artifact artifact = dependencyNode.getArtifact(); + + if (state == DependencyNode.INCLUDED && !Artifact.SCOPE_TEST.equals(artifact.getScope())) { + String canonicalName = artifact.getGroupId() + ":" + artifact.getArtifactId(); + if (artifacts.contains(canonicalName)) { + included.add(canonicalName); + } + } + } + + return included; + } + + private void synchronizeParentPom() throws Exception { + File pomFile = new File(allStartersDir(), "pom.xml"); + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document pom = builder.parse(pomFile); + + XPath xpath = XPathFactory.newInstance().newXPath(); + Node modules = ((NodeList) xpath.compile("/project/modules").evaluate(pom, XPathConstants.NODESET)).item(0); + + // cleanup current modules + while (modules.hasChildNodes()) { + modules.removeChild(modules.getFirstChild()); + } + + for (File starterDir : Arrays.asList(allStartersDir().listFiles((f, n) -> f.isDirectory() && n.endsWith(SpringBootHelper.STARTER_SUFFIX))).stream().sorted().collect(Collectors.toList())) { + Node module = pom.createElement("module"); + module.setTextContent(starterDir.getName()); + modules.appendChild(module); + } + + writeXmlFormatted(pom, pomFile); + } + + private Document createBasePom() throws Exception { + Template pomTemplate = getTemplate("spring-boot-starter-template-pom.xml"); + Map<String, String> props = new HashMap<>(); + props.put("version", project.getVersion()); + props.put("componentId", getComponentId()); + props.put("componentName", project.getName()); + props.put("componentDescription", project.getDescription()); + + StringWriter sw = new StringWriter(); + pomTemplate.process(props, sw); + + String xml = sw.toString(); + ByteArrayInputStream bin = new ByteArrayInputStream(xml.getBytes("UTF-8")); + + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document pom = builder.parse(bin); + return pom; + } + + private void writeSpringProvides() throws IOException, TemplateException { + Template fileTemplate = getTemplate("spring-boot-starter-template-spring.provides"); + Map<String, String> props = new HashMap<>(); + props.put("artifactId", project.getArtifactId()); + + File outDir = new File(starterDir(), "src/main/resources/META-INF"); + outDir.mkdirs(); + File outFile = new File(outDir, "spring.provides"); + + try (FileWriter outWriter = new FileWriter(outFile)) { + fileTemplate.process(props, outWriter); + } + } + + + private Template getTemplate(String name) throws IOException { + Configuration cfg = new Configuration(Configuration.getVersion()); + + cfg.setTemplateLoader(new URLTemplateLoader() { + @Override + protected URL getURL(String name) { + return SpringBootStarterMojo.class.getResource("/" + name); + } + }); + + cfg.setDefaultEncoding("UTF-8"); + Template template = cfg.getTemplate(name); + return template; + } + + + private boolean isStarterAllowed() { + + for (String ignored : IGNORE_MODULES) { + if (ignored.equals(project.getArtifactId())) { + getLog().info("Spring-Boot-Starter: component inside ignore list"); + return false; + } + } + + if (IGNORE_TEST_MODULES && project.getArtifactId().startsWith("camel-test-")) { + getLog().info("Spring-Boot-Starter: test components are ignored"); + return false; + } + + if (project.getPackaging() != null && !project.getPackaging().equals("jar")) { + getLog().info("Spring-Boot-Starter: ignored for wrong packaging"); + return false; + } + + // include 'camel-core' + if (baseDir.getName().equals("camel-core")) { + return true; + } + + // Build a starter for all components under the 'components' dir and include submodules ending with '-component' + if (baseDir.getParentFile().getName().equals("components") || baseDir.getName().endsWith("-component")) { + return true; + } + + getLog().info("Spring-Boot-Starter: component directory mismatch"); + return false; + } + + + private String getComponentId() { + String componentName = project.getArtifact().getArtifactId(); + String componentId = componentName.replace("camel-", ""); + return componentId; + } + + private void writeXmlFormatted(Document xml, File destination) throws Exception { + + OutputFormat format = new OutputFormat(xml); + format.setLineWidth(200); + format.setIndenting(true); + format.setIndent(4); + + StringWriter sw = new StringWriter(); + XMLSerializer serializer = new XMLSerializer(sw, format); + serializer.serialize(xml); + + // Fix the output (cannot find a good serializer) + // The apache header is put in the wrong location + StringBuilder b = new StringBuilder(sw.toString()); + int lastTagLoc = b.lastIndexOf("<"); + int lastCloseHeaderLoc = b.lastIndexOf("-->"); + if (lastCloseHeaderLoc > lastTagLoc) { + // The apache header has been put at the end + int headerLoc = b.lastIndexOf("<!--"); + String apacheHeader = b.substring(headerLoc, lastCloseHeaderLoc + 3); + b.delete(headerLoc, lastCloseHeaderLoc + 3); + + int pos = b.indexOf("?>"); + if (pos > 0) { + b.insert(pos + 2, "\n" + apacheHeader); + } else { + b.insert(0, apacheHeader); + } + } + + + try (Writer out = new FileWriter(destination)) { + IOUtils.write(b.toString(), out); + } + + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-additional-dependencies.properties ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-additional-dependencies.properties b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-additional-dependencies.properties new file mode 100644 index 0000000..21bf49f --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-additional-dependencies.properties @@ -0,0 +1,3 @@ +# This file contains additional dependencies needed by camel modules in a spring-boot deployment + +camel-kubernetes=org.hibernate:hibernate-validator \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-pom.xml ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-pom.xml b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-pom.xml new file mode 100644 index 0000000..f753f25 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-pom.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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 + + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components-starter</artifactId> + <version>${version}</version> + </parent> + + <artifactId>camel-${componentId}-starter</artifactId> + <packaging>jar</packaging> + <name>Spring-Boot Starter :: ${componentName}</name> + <description>Spring-Boot Starter for ${componentDescription}</description> + + <dependencies> + + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-spring-boot-starter</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-${componentId}</artifactId> + </dependency> + + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-spring.provides ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-spring.provides b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-spring.provides new file mode 100644 index 0000000..8a6df26 --- /dev/null +++ b/tooling/maven/camel-package-maven-plugin/src/main/resources/spring-boot-starter-template-spring.provides @@ -0,0 +1,18 @@ +# +# 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 +# +# 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. +# + +provides: ${artifactId} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/camel/blob/67633827/tooling/parent/pom.xml ---------------------------------------------------------------------- diff --git a/tooling/parent/pom.xml b/tooling/parent/pom.xml index 71a1941..d01ec4b 100644 --- a/tooling/parent/pom.xml +++ b/tooling/parent/pom.xml @@ -31,9 +31,11 @@ <packaging>pom</packaging> <properties> + <maven-version>2.2.1</maven-version> <maven-maven-plugin-descriptor-version>2.2.1</maven-maven-plugin-descriptor-version> <maven-project-version>2.2.1</maven-project-version> + <maven-dependency-tree-version>2.2</maven-dependency-tree-version> <maven-reporting-api-version>2.2.1</maven-reporting-api-version> <maven-reporting-impl-version>2.4</maven-reporting-impl-version> <plexus-build-api-version>0.0.7</plexus-build-api-version> @@ -89,6 +91,11 @@ <version>${maven-project-version}</version> </dependency> <dependency> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-dependency-tree</artifactId> + <version>${maven-dependency-tree-version}</version> + </dependency> + <dependency> <groupId>org.apache.maven.reporting</groupId> <artifactId>maven-reporting-api</artifactId> <version>${maven-reporting-api-version}</version>