This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch version-list in repository https://gitbox.apache.org/repos/asf/camel.git
commit ccf3ce87cdf39a767ae4dfb09a02b99ba758766c Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Mar 12 15:07:46 2023 +0100 CAMEL-19128: camel-jbang - Add version command --- .../dsl/jbang/core/commands/CamelJBangMain.java | 8 +- .../core/commands/version/VersionCommand.java | 37 ++++++ .../jbang/core/commands/version/VersionGet.java | 54 ++++++++ .../jbang/core/commands/version/VersionList.java | 138 +++++++++++++++++++++ .../camel/dsl/jbang/core/common/VersionHelper.java | 36 +++++- .../camel/main/download/DependencyDownloader.java | 12 ++ .../main/download/MavenDependencyDownloader.java | 71 +++++++++++ .../java/org/apache/camel/main/util/XmlHelper.java | 63 ++++++++++ 8 files changed, 412 insertions(+), 7 deletions(-) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java index 4726368bb34..14814ad39d6 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java @@ -67,6 +67,9 @@ import org.apache.camel.dsl.jbang.core.commands.process.ListService; import org.apache.camel.dsl.jbang.core.commands.process.ListTrace; import org.apache.camel.dsl.jbang.core.commands.process.ListVault; import org.apache.camel.dsl.jbang.core.commands.process.StopProcess; +import org.apache.camel.dsl.jbang.core.commands.version.VersionCommand; +import org.apache.camel.dsl.jbang.core.commands.version.VersionGet; +import org.apache.camel.dsl.jbang.core.commands.version.VersionList; import org.apache.camel.dsl.jbang.core.common.CommandLineHelper; import picocli.CommandLine; import picocli.CommandLine.Command; @@ -136,7 +139,10 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("list", new CommandLine(new ConfigList(main))) .addSubcommand("get", new CommandLine(new ConfigGet(main))) .addSubcommand("unset", new CommandLine(new ConfigUnset(main))) - .addSubcommand("set", new CommandLine(new ConfigSet(main)))); + .addSubcommand("set", new CommandLine(new ConfigSet(main)))) + .addSubcommand("version", new CommandLine(new VersionCommand(main)) + .addSubcommand("get", new CommandLine(new VersionGet(main))) + .addSubcommand("list", new CommandLine(new VersionList(main)))); commandLine.getCommandSpec().versionProvider(() -> { CamelCatalog catalog = new DefaultCamelCatalog(); diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java new file mode 100644 index 00000000000..d305b45866c --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java @@ -0,0 +1,37 @@ +/* + * 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.dsl.jbang.core.commands.version; + +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import picocli.CommandLine; + +@CommandLine.Command(name = "version", + description = "Manage Camel versions (use config --help to see sub commands)") +public class VersionCommand extends CamelCommand { + + public VersionCommand(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + // defaults to get + new CommandLine(new VersionGet(getMain())).execute(); + return 0; + } +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.java new file mode 100644 index 00000000000..23d0a283650 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.java @@ -0,0 +1,54 @@ +/* + * 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.dsl.jbang.core.commands.version; + +import org.apache.camel.catalog.CamelCatalog; +import org.apache.camel.catalog.DefaultCamelCatalog; +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.common.CommandLineHelper; +import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates; +import picocli.CommandLine; + +@CommandLine.Command(name = "get", description = "Displays current Camel version") +public class VersionGet extends CamelCommand { + + @CommandLine.Option(names = { "--runtime" }, completionCandidates = RuntimeCompletionCandidates.class, + description = "Runtime (spring-boot, quarkus, or camel-main)") + protected String runtime; + + public VersionGet(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + CamelCatalog catalog = new DefaultCamelCatalog(); + String v = catalog.getCatalogVersion(); + System.out.println("Camel CLI version: " + v); + + CommandLineHelper.loadProperties(properties -> { + String uv = properties.getProperty("camel-version"); + if (uv != null) { + System.out.println("User configuration Camel version: " + uv); + } + }); + + return 0; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java new file mode 100644 index 00000000000..7e6ab2be88c --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java @@ -0,0 +1,138 @@ +/* + * 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.dsl.jbang.core.commands.version; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import com.github.freva.asciitable.AsciiTable; +import com.github.freva.asciitable.Column; +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates; +import org.apache.camel.dsl.jbang.core.common.VersionHelper; +import org.apache.camel.main.KameletMain; +import org.apache.camel.main.download.MavenDependencyDownloader; +import picocli.CommandLine; + +@CommandLine.Command(name = "list", description = "Displays available Camel versions") +public class VersionList extends CamelCommand { + + private static final String MINIMUM_VERSION = "3.14.0"; + private static final String MINIMUM_QUARKUS_VERSION = "2.13.0"; + + // TODO: Filter for minimum camel version + // TODO: grab Q and SB runtime version + + @CommandLine.Option(names = { "--sort" }, + description = "Sort by version", defaultValue = "version") + String sort; + + @CommandLine.Option(names = { "--runtime" }, completionCandidates = RuntimeCompletionCandidates.class, + description = "Runtime (spring-boot, quarkus, or camel-main)") + protected String runtime; + + @CommandLine.Option(names = { "--repo", "--repos" }, description = "Maven repository for downloading available versions") + String repo; + + public VersionList(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + configureLoggingOff(); + KameletMain main = new KameletMain(); + + List<String> versions; + try { + main.start(); + + // use kamelet-main to download from maven + MavenDependencyDownloader downloader = main.getCamelContext().hasService(MavenDependencyDownloader.class); + + String g = "org.apache.camel"; + String a = "camel-catalog"; + if ("spring-boot".equalsIgnoreCase(runtime)) { + g = "org.apache.camel.springboot"; + a = "camel-catalog-provider-springboot"; + } else if ("quarkus".equalsIgnoreCase(runtime)) { + g = "org.apache.camel.quarkus"; + a = "camel-quarkus-catalog"; + } + + versions = downloader.downloadAvailableVersions(g, a, repo); + versions = versions.stream().filter(this::acceptVersion).collect(Collectors.toList()); + + main.stop(); + } catch (Exception e) { + System.out.println("Error downloading available Camel versions"); + return 1; + } + + List<Row> rows = new ArrayList<>(); + for (String v : versions) { + Row row = new Row(); + rows.add(row); + row.coreVersion = v; + } + + // sort rows + rows.sort(this::sortRow); + + System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList( + new Column().header("QUARKUS VERSION").visible("quarkus".equalsIgnoreCase(runtime)).with(r -> r.runtimeVersion), + new Column().header("SPRING BOOT VERSION").visible("spring-boot".equalsIgnoreCase(runtime)) + .with(r -> r.runtimeVersion), + new Column().header("CAMEL VERSION").with(r -> r.coreVersion)))); + + return 0; + } + + protected int sortRow(Row o1, Row o2) { + String s = sort; + int negate = 1; + if (s.startsWith("-")) { + s = s.substring(1); + negate = -1; + } + switch (s) { + case "version": + String v1 = o1.runtimeVersion != null ? o1.runtimeVersion : o1.coreVersion; + String v2 = o2.runtimeVersion != null ? o2.runtimeVersion : o2.coreVersion; + return VersionHelper.compare(v1, v2) * negate; + default: + return 0; + } + } + + private boolean acceptVersion(String version) { + String min = MINIMUM_VERSION; + if ("quarkus".equalsIgnoreCase(runtime)) { + min = MINIMUM_QUARKUS_VERSION; + } + return VersionHelper.isGE(version, min); + } + + private static class Row { + String runtimeVersion; + String coreVersion; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java index cc22c2985e5..97ae5734405 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java @@ -24,8 +24,12 @@ public final class VersionHelper { } public static boolean isGE(String source, String target) { + return compare(source, target) >= 0; + } + + public static int compare(String source, String target) { if (source == null || target == null) { - return false; + return 0; } String s1 = StringHelper.before(source, "."); String s2 = StringHelper.after(source, "."); @@ -39,7 +43,18 @@ public final class VersionHelper { t1 = StringHelper.before(target, ","); t2 = StringHelper.after(target, ","); } - + String s3 = StringHelper.after(s2, "."); + if (s3 != null) { + s2 = StringHelper.before(s2, "."); + } else { + s3 = ""; + } + String t3 = StringHelper.after(t2, "."); + if (t3 != null) { + t2 = StringHelper.before(t2, "."); + } else { + t3 = ""; + } // convert to 2-digit numbers if (s1.length() < 2) { s1 = "0" + s1; @@ -47,16 +62,25 @@ public final class VersionHelper { if (s2.length() < 2) { s2 = "0" + s2; } + if (s2.length() < 2) { + s2 = "0" + s2; + } + if (s3.length() < 2) { + s3 = "0" + s3; + } if (t1.length() < 2) { t1 = "0" + t1; } if (t2.length() < 2) { t2 = "0" + t2; } + if (t3.length() < 2) { + t3 = "0" + t3; + } - String s = s1 + s2; - String t = t1 + t2; - int n = s.compareTo(t); - return n >= 0; + String s = s1 + s2 + s3; + String t = t1 + t2 + t3; + return s.compareTo(t); } + } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java index 1ebf173e5a3..ccab96ea85f 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java @@ -16,6 +16,8 @@ */ package org.apache.camel.main.download; +import java.util.List; + import org.apache.camel.CamelContextAware; import org.apache.camel.StaticService; @@ -120,6 +122,16 @@ public interface DependencyDownloader extends CamelContextAware, StaticService { */ MavenArtifact downloadArtifact(String groupId, String artifactId, String version); + /** + * Downloads the available versions for the given maven artifact + * + * @param groupId maven group id + * @param artifactId maven artifact id + * @param repo to use specific maven repository instead of maven central + * @return list of versions of the given artifact + */ + List<String> downloadAvailableVersions(String groupId, String artifactId, String repo); + /** * Checks whether the dependency is already on the classpath * diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java index 41a3b48ef9f..362c52e82e1 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java @@ -37,9 +37,17 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.main.injection.DIRegistry; +import org.apache.camel.main.util.XmlHelper; import org.apache.camel.support.service.ServiceHelper; import org.apache.camel.support.service.ServiceSupport; import org.apache.camel.util.FileUtil; @@ -148,6 +156,8 @@ import org.eclipse.aether.internal.impl.synccontext.named.NameMapper; import org.eclipse.aether.internal.impl.synccontext.named.NameMappers; import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactory; import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactoryImpl; +import org.eclipse.aether.metadata.DefaultMetadata; +import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.named.NamedLockFactory; import org.eclipse.aether.named.providers.FileLockNamedLockFactory; import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory; @@ -164,6 +174,8 @@ import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResolutionException; import org.eclipse.aether.resolution.DependencyResult; +import org.eclipse.aether.resolution.MetadataRequest; +import org.eclipse.aether.resolution.MetadataResult; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; @@ -452,6 +464,27 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende return null; } + @Override + public List<String> downloadAvailableVersions(String groupId, String artifactId, String repo) { + String gav = groupId + ":" + artifactId; + LOG.debug("DownloadAvailableVersions: {}", gav); + + // repo 0 is maven central + RemoteRepository repository = remoteRepositories.get(0); + if (repo != null) { + List<RemoteRepository> extra = resolveExtraRepositories(repo); + if (!extra.isEmpty()) { + repository = extra.get(0); + } + } + List<String> versions = resolveAvailableVersions(groupId, artifactId, repository); + if (LOG.isDebugEnabled()) { + LOG.debug("DownloadAvailableVersions {} -> [{}]", gav, versions); + } + + return versions; + } + public boolean alreadyOnClasspath(String groupId, String artifactId, String version) { return alreadyOnClasspath(groupId, artifactId, version, true); } @@ -1290,6 +1323,44 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } } + public List<String> resolveAvailableVersions( + String groupId, String artifactId, RemoteRepository repository) { + + List<String> answer = new ArrayList<>(); + + try { + MetadataRequest ar = new MetadataRequest(); + ar.setRepository(repository); + ar.setFavorLocalRepository(false); + ar.setMetadata(new DefaultMetadata(groupId, artifactId, "maven-metadata.xml", Metadata.Nature.RELEASE)); + + List<MetadataResult> result = repositorySystem.resolveMetadata(repositorySystemSession, List.of(ar)); + for (MetadataResult mr : result) { + if (mr.isResolved() && mr.getMetadata().getFile() != null) { + File f = mr.getMetadata().getFile(); + if (f.exists() && f.isFile()) { + DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document dom = db.parse(f); + NodeList nl = dom.getElementsByTagName("version"); + for (int i = 0; i < nl.getLength(); i++) { + Element node = (Element) nl.item(i); + String v = node.getTextContent(); + if (v != null) { + answer.add(v); + } + } + } + } + } + } catch (Exception e) { + String msg = "Cannot resolve available versions in " + repository.getUrl(); + throw new DownloadException(msg, e); + } + + return answer; + } + private static class AcceptAllDependencyFilter implements DependencyFilter { @Override public boolean accept(DependencyNode node, List<DependencyNode> parents) { diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java new file mode 100644 index 00000000000..52b843abfcf --- /dev/null +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java @@ -0,0 +1,63 @@ +/* + * 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.main.util; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.camel.util.ObjectHelper; + +public final class XmlHelper { + + private XmlHelper() { + } + + public static DocumentBuilderFactory createDocumentBuilderFactory() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + try { + // Set secure processing + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); + } catch (ParserConfigurationException e) { + } + try { + // Disable the external-general-entities by default + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + } catch (ParserConfigurationException e) { + } + try { + // Disable the external-parameter-entities by default + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (ParserConfigurationException e) { + } + // setup the SecurityManager by default if it's apache xerces + try { + Class<?> smClass = ObjectHelper.loadClass("org.apache.xerces.util.SecurityManager"); + if (smClass != null) { + Object sm = smClass.getDeclaredConstructor().newInstance(); + // Here we just use the default setting of the SeurityManager + factory.setAttribute("http://apache.org/xml/properties/security-manager", sm); + } + } catch (Exception e) { + } + return factory; + } + +}