You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/04/01 13:33:15 UTC
[camel] 01/02: CAMEL-16419: camel-maven-plugin - Add prepare-fatjar
goal for better support of packaging Camel JARs to a fat-jar
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 64c5464181f716a35f868c98bab991801c3bc6d9
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Apr 1 15:22:12 2021 +0200
CAMEL-16419: camel-maven-plugin - Add prepare-fatjar goal for better support of packaging Camel JARs to a fat-jar
---
.../impl/converter/BaseTypeConverterRegistry.java | 22 +-
.../org/apache/camel/maven/DynamicClassLoader.java | 40 ++++
.../org/apache/camel/maven/PrepareFatJarMojo.java | 262 +++++++++++++++++++++
3 files changed, 317 insertions(+), 7 deletions(-)
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java b/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
index 15b7c26..3c0396b 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/converter/BaseTypeConverterRegistry.java
@@ -51,6 +51,8 @@ import org.slf4j.LoggerFactory;
*/
public abstract class BaseTypeConverterRegistry extends CoreTypeConverterRegistry {
+ private static final String META_INF_SERVICES_UBER_TYPE_CONVERTER_LOADER
+ = "META-INF/services/org/apache/camel/UberTypeConverterLoader";
public static final String META_INF_SERVICES_TYPE_CONVERTER_LOADER
= "META-INF/services/org/apache/camel/TypeConverterLoader";
public static final String META_INF_SERVICES_FALLBACK_TYPE_CONVERTER
@@ -171,14 +173,21 @@ public abstract class BaseTypeConverterRegistry extends CoreTypeConverterRegistr
/**
* Finds the type converter loader classes from the classpath looking for text files on the classpath at the
- * {@link #META_INF_SERVICES_TYPE_CONVERTER_LOADER} location.
+ * {@link #META_INF_SERVICES_UBER_TYPE_CONVERTER_LOADER} and {@link #META_INF_SERVICES_TYPE_CONVERTER_LOADER}
+ * locations.
*/
protected Collection<String> findTypeConverterLoaderClasses() throws IOException {
- Set<String> loaders = new LinkedHashSet<>();
- Collection<URL> loaderResources = getLoaderUrls();
+ Collection<String> loaders = new LinkedHashSet<>();
+ findTypeConverterLoaderClasses(loaders, META_INF_SERVICES_UBER_TYPE_CONVERTER_LOADER);
+ findTypeConverterLoaderClasses(loaders, META_INF_SERVICES_TYPE_CONVERTER_LOADER);
+ return loaders;
+ }
+
+ protected void findTypeConverterLoaderClasses(Collection<String> loaders, String basePath) throws IOException {
+ Collection<URL> loaderResources = getLoaderUrls(basePath);
for (URL url : loaderResources) {
LOG.debug("Loading file {} to retrieve list of type converters, from url: {}",
- META_INF_SERVICES_TYPE_CONVERTER_LOADER, url);
+ basePath, url);
BufferedReader reader = IOHelper.buffered(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
String line;
do {
@@ -189,13 +198,12 @@ public abstract class BaseTypeConverterRegistry extends CoreTypeConverterRegistr
} while (line != null);
IOHelper.close(reader);
}
- return loaders;
}
- protected Collection<URL> getLoaderUrls() throws IOException {
+ protected Collection<URL> getLoaderUrls(String basePath) throws IOException {
List<URL> loaderResources = new ArrayList<>();
for (ClassLoader classLoader : resolver.getClassLoaders()) {
- Enumeration<URL> resources = classLoader.getResources(META_INF_SERVICES_TYPE_CONVERTER_LOADER);
+ Enumeration<URL> resources = classLoader.getResources(basePath);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
loaderResources.add(url);
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DynamicClassLoader.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DynamicClassLoader.java
new file mode 100644
index 0000000..5d9d669
--- /dev/null
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DynamicClassLoader.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+import java.util.List;
+
+class DynamicClassLoader extends URLClassLoader {
+
+ public DynamicClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ public static DynamicClassLoader createDynamicClassLoaderFromUrls(List<URL> classpathElements) {
+ final URL[] urls = new URL[classpathElements.size()];
+ int i = 0;
+ for (Iterator<URL> it = classpathElements.iterator(); it.hasNext(); i++) {
+ urls[i] = it.next();
+ }
+ // no parent classloader as we only want to load from the given URLs
+ return new DynamicClassLoader(urls, null);
+ }
+
+}
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/PrepareFatJarMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/PrepareFatJarMojo.java
new file mode 100644
index 0000000..8a3d43e
--- /dev/null
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/PrepareFatJarMojo.java
@@ -0,0 +1,262 @@
+/*
+ * 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;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+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;
+
+@Mojo(name = "prepare-fatjar", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE,
+ defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
+public class PrepareFatJarMojo extends AbstractMojo {
+
+ private static final String GENERATED_MSG = "Generated by camel build tools - do NOT edit this file!";
+ private static final String NL = "\n";
+
+ private static final String META_INF_SERVICES_TYPE_CONVERTER_LOADER
+ = "META-INF/services/org/apache/camel/TypeConverterLoader";
+
+ private static final String META_INF_SERVICES_UBER_TYPE_CONVERTER_LOADER
+ = "META-INF/services/org/apache/camel/UberTypeConverterLoader";
+
+ private DynamicClassLoader projectClassLoader;
+
+ @Parameter(property = "project", required = true, readonly = true)
+ private MavenProject project;
+ @Parameter(defaultValue = "${project.build.outputDirectory}")
+ private File classesDirectory;
+ @Component
+ private ArtifactFactory artifactFactory;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ Collection<String> loaders = findTypeConverterLoaderClasses();
+ if (loaders.isEmpty()) {
+ return;
+ }
+
+ getLog().info("Found " + loaders.size() + " Camel type converter loaders from project classpath");
+
+ // prepare output to generate
+ StringBuilder sb = new StringBuilder();
+ sb.append("# ");
+ sb.append(GENERATED_MSG);
+ sb.append(NL);
+ sb.append(String.join(NL, loaders));
+ sb.append(NL);
+
+ String data = sb.toString();
+
+ File file = new File(classesDirectory, META_INF_SERVICES_UBER_TYPE_CONVERTER_LOADER);
+ try {
+ writeFile(file, data);
+ } catch (IOException e) {
+ throw new MojoFailureException("Error updating " + file, e);
+ }
+ }
+
+ private void writeFile(File file, String data) throws IOException {
+ Path path = file.toPath();
+ Files.createDirectories(path.getParent());
+ Files.write(path, data.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ }
+
+ /**
+ * Finds the type converter loader classes from the classpath looking for text files on the classpath at the
+ * {@link #META_INF_SERVICES_TYPE_CONVERTER_LOADER} location.
+ */
+ protected Collection<String> findTypeConverterLoaderClasses() {
+ Set<String> loaders = new LinkedHashSet<>();
+
+ try {
+ Enumeration<URL> loaderResources = getProjectClassLoader().getResources(META_INF_SERVICES_TYPE_CONVERTER_LOADER);
+ while (loaderResources.hasMoreElements()) {
+ URL url = loaderResources.nextElement();
+ getLog().debug("Loading file " + META_INF_SERVICES_TYPE_CONVERTER_LOADER
+ + " to retrieve list of type converters, from url: " + url);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
+ String line;
+ do {
+ line = reader.readLine();
+ if (line != null && !line.startsWith("#") && !line.isEmpty()) {
+ loaders.add(line);
+ }
+ } while (line != null);
+ try {
+ reader.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ } catch (Exception e) {
+ getLog().warn("Error finding type converters due to " + e.getMessage());
+ }
+
+ return loaders;
+ }
+
+ protected final DynamicClassLoader getProjectClassLoader() throws MojoExecutionException {
+ if (projectClassLoader == null) {
+ List<URL> urls = new ArrayList<>();
+ // need to include project compile dependencies (code similar to camel-maven-plugin)
+ addRelevantProjectDependenciesToClasspath(urls, false);
+ projectClassLoader = DynamicClassLoader.createDynamicClassLoaderFromUrls(urls);
+ }
+ return projectClassLoader;
+ }
+
+ /**
+ * Add any relevant project dependencies to the classpath. Takes includeProjectDependencies into consideration.
+ *
+ * @param path classpath of {@link URL} objects
+ */
+ private void addRelevantProjectDependenciesToClasspath(List<URL> path, boolean testClasspathOnly)
+ throws MojoExecutionException {
+ try {
+ getLog().debug("Project Dependencies will be included.");
+
+ if (testClasspathOnly) {
+ URL testClasses = new File(project.getBuild().getTestOutputDirectory()).toURI().toURL();
+ getLog().debug("Adding to classpath : " + testClasses);
+ path.add(testClasses);
+ } else {
+ URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL();
+ getLog().debug("Adding to classpath : " + mainClasses);
+ path.add(mainClasses);
+ }
+
+ Set<Artifact> dependencies = project.getArtifacts();
+
+ // system scope dependencies are not returned by maven 2.0. See
+ // MEXEC-17
+ dependencies.addAll(getAllNonTestScopedDependencies());
+
+ Iterator<Artifact> iter = dependencies.iterator();
+ while (iter.hasNext()) {
+ Artifact classPathElement = iter.next();
+ getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId()
+ + " to classpath");
+ File file = classPathElement.getFile();
+ if (file != null) {
+ path.add(file.toURI().toURL());
+ }
+ }
+
+ } catch (MalformedURLException e) {
+ throw new MojoExecutionException("Error during setting up classpath", e);
+ }
+ }
+
+ private Collection<Artifact> getAllNonTestScopedDependencies() throws MojoExecutionException {
+ List<Artifact> answer = new ArrayList<>();
+
+ for (Artifact artifact : getAllDependencies()) {
+
+ // do not add test artifacts
+ if (!artifact.getScope().equals(Artifact.SCOPE_TEST)) {
+ answer.add(artifact);
+ }
+ }
+ return answer;
+ }
+
+ // generic method to retrieve all the transitive dependencies
+ private Collection<Artifact> getAllDependencies() throws MojoExecutionException {
+ List<Artifact> artifacts = new ArrayList<>();
+
+ for (Iterator<?> dependencies = project.getDependencies().iterator(); dependencies.hasNext();) {
+ Dependency dependency = (Dependency) dependencies.next();
+
+ String groupId = dependency.getGroupId();
+ String artifactId = dependency.getArtifactId();
+
+ VersionRange versionRange;
+ try {
+ versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
+ } catch (InvalidVersionSpecificationException e) {
+ throw new MojoExecutionException("unable to parse version", e);
+ }
+
+ String type = dependency.getType();
+ if (type == null) {
+ type = "jar";
+ }
+ String classifier = dependency.getClassifier();
+ boolean optional = dependency.isOptional();
+ String scope = dependency.getScope();
+ if (scope == null) {
+ scope = Artifact.SCOPE_COMPILE;
+ }
+
+ if (this.artifactFactory != null) {
+ Artifact art = this.artifactFactory.createDependencyArtifact(groupId, artifactId, versionRange,
+ type, classifier, scope, null, optional);
+
+ if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
+ art.setFile(new File(dependency.getSystemPath()));
+ }
+
+ List<String> exclusions = new ArrayList<>();
+ for (Exclusion exclusion : dependency.getExclusions()) {
+ exclusions.add(exclusion.getGroupId() + ":" + exclusion.getArtifactId());
+ }
+
+ ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions);
+
+ art.setDependencyFilter(newFilter);
+
+ artifacts.add(art);
+ }
+ }
+
+ return artifacts;
+ }
+
+}