Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1251,11 +1251,20 @@ camel plugin get --all
----
Supported plugins:

NAME COMMAND DEPENDENCY DESCRIPTION
kubernetes kubernetes org.apache.camel:camel-jbang-plugin-kubernetes Run Camel applications on Kubernetes
generate generate org.apache.camel:camel-jbang-plugin-generate Generate code such as DTOs
NAME COMMAND VENDOR DEPENDENCY DESCRIPTION
kubernetes kubernetes ASF org.apache.camel:camel-jbang-plugin-kubernetes Run Camel applications on Kubernetes
generate generate ASF org.apache.camel:camel-jbang-plugin-generate Generate code such as DTOs
...

Known 3rd party plugins:

NAME COMMAND VENDOR DEPENDENCY DESCRIPTION
forage forage Community io.kaoto.forage:camel-jbang-plugin-forage Utilities for working with Forage components
camel-kit kit Community io.github.luigidemasi:camel-kit-jbang-plugin Design Apache Camel Integrations with AI
----

The `VENDOR` column indicates whether the plugin is from `ASF` (Apache Software Foundation) or `Community` (3rd party).

In case you want to enable a plugin and its functionality, you can add it as follows:

[source,bash]
Expand All @@ -1272,6 +1281,20 @@ camel plugin add generate

This adds the plugin, and all subcommands are now available for execution.

Known 3rd party plugins can be installed by name as well. For example:

[source,bash]
----
camel plugin add forage
----

To install a specific version of a 3rd party plugin, use the `--version` option:

[source,bash]
----
camel plugin add forage --version=1.2.3
----

You can list the currently installed plugins with:

[source,bash]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.camel.catalog.CamelCatalog;
import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.common.PluginHelper;
import org.apache.camel.dsl.jbang.core.common.PluginType;
import org.apache.camel.util.json.JsonObject;
import picocli.CommandLine;
Expand Down Expand Up @@ -89,6 +90,30 @@ public Integer doCall() throws Exception {
if (firstVersion == null) {
firstVersion = camelPlugin.get().getFirstVersion();
}
} else {
Optional<JsonObject> known = PluginHelper.findKnownPlugin(name);
if (known.isPresent()) {
JsonObject kp = known.get();
if (command == null) {
command = kp.getString("command");
}
if (description == null) {
description = kp.getString("description");
}
if (firstVersion == null) {
firstVersion = kp.getString("firstVersion");
}
if (gav == null) {
groupId = kp.getStringOrDefault("groupId", groupId);
artifactId = kp.getString("artifactId");
}
if (repositories == null) {
String knownRepos = kp.getString("repos");
if (knownRepos != null) {
repositories = knownRepos;
}
}
}
}

if (command == null) {
Expand All @@ -111,8 +136,12 @@ public Integer doCall() throws Exception {

if (gav == null && (groupId != null && artifactId != null)) {
if (version == null) {
CamelCatalog catalog = new DefaultCamelCatalog();
version = catalog.getCatalogVersion();
if ("org.apache.camel".equals(groupId)) {
CamelCatalog catalog = new DefaultCamelCatalog();
version = catalog.getCatalogVersion();
} else {
version = "LATEST";
}
}

gav = "%s:%s:%s".formatted(groupId, artifactId, version);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.github.freva.asciitable.HorizontalAlign;
import com.github.freva.asciitable.OverflowBehaviour;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.common.PluginHelper;
import org.apache.camel.dsl.jbang.core.common.PluginType;
import org.apache.camel.util.json.JsonObject;
import picocli.CommandLine;
Expand Down Expand Up @@ -59,7 +60,8 @@ public Integer doCall() throws Exception {
= details.getStringOrDefault("description", "Plugin %s called with command %s".formatted(name, command));
String repos = details.getString("repos");

rows.add(new Row(name, command, dependency, description, repos));
String vendor = resolveVendor(name);
rows.add(new Row(name, command, dependency, description, repos, vendor));
});

printRows(rows);
Expand All @@ -71,7 +73,7 @@ public Integer doCall() throws Exception {
String dependency = "org.apache.camel:camel-jbang-plugin-%s".formatted(camelPlugin.getCommand());
rows.add(new Row(
camelPlugin.getName(), camelPlugin.getCommand(), dependency,
camelPlugin.getDescription(), camelPlugin.getRepos()));
camelPlugin.getDescription(), camelPlugin.getRepos(), camelPlugin.getVendor()));
}
}

Expand All @@ -82,18 +84,50 @@ public Integer doCall() throws Exception {

printRows(rows);
}

rows.clear();
List<JsonObject> knownPlugins = PluginHelper.loadKnownPlugins();
for (JsonObject kp : knownPlugins) {
String kpName = kp.getString("name");
if (plugins.get(kpName) == null && PluginType.findByName(kpName).isEmpty()) {
String dep = kp.getString("groupId") != null && kp.getString("artifactId") != null
? "%s:%s".formatted(kp.getString("groupId"), kp.getString("artifactId"))
: kp.getStringOrDefault("dependency", "");
rows.add(new Row(
kpName, kp.getString("command"), dep,
kp.getString("description"), kp.getString("repos"),
kp.getStringOrDefault("vendor", "Community")));
}
}

if (!rows.isEmpty()) {
printer().println();
printer().println("Known 3rd party plugins:");
printer().println();

printRows(rows);
}
}

return 0;
}

private String resolveVendor(String name) {
return PluginType.findByName(name)
.map(PluginType::getVendor)
.or(() -> PluginHelper.findKnownPlugin(name).map(kp -> kp.getStringOrDefault("vendor", "Community")))
.orElse("");
}

private void printRows(List<Row> rows) {
if (!rows.isEmpty()) {
printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
new Column().header("NAME").headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
.with(r -> r.name),
new Column().header("COMMAND").headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
.with(r -> r.command),
new Column().header("VENDOR").headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
.with(r -> r.vendor != null ? r.vendor : ""),
new Column().header("DEPENDENCY").headerAlign(HorizontalAlign.LEFT).dataAlign(HorizontalAlign.LEFT)
.with(r -> r.dependency),
new Column().visible(repos).header("REPOSITORY").headerAlign(HorizontalAlign.LEFT)
Expand All @@ -105,6 +139,6 @@ private void printRows(List<Row> rows) {
}
}

private record Row(String name, String command, String dependency, String description, String repos) {
private record Row(String name, String command, String dependency, String description, String repos, String vendor) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.apache.camel.support.ObjectHelper;
import org.apache.camel.tooling.maven.MavenGav;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
import org.apache.camel.util.json.Jsoner;
import picocli.CommandLine;
Expand Down Expand Up @@ -575,6 +576,37 @@ public static void savePluginConfig(JsonObject plugins) {
}
}

/**
* Loads the list of known 3rd party plugins from the classpath resource known-plugins.json.
*/
public static List<JsonObject> loadKnownPlugins() {
try (InputStream is = PluginHelper.class.getClassLoader().getResourceAsStream("known-plugins.json")) {
if (is != null) {
String text = new String(is.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);
JsonArray arr = (JsonArray) Jsoner.deserialize(text);
List<JsonObject> result = new ArrayList<>(arr.size());
String text = new String(is.readAllBytes(), StandardCharsets.UTF_8);
if (o instanceof JsonObject jo) {
result.add(jo);
}
}
return result;
}
} catch (Exception e) {
// ignore
}
return List.of();
}

/**
* Finds a known 3rd party plugin by name (case-insensitive).
*/
public static Optional<JsonObject> findKnownPlugin(String name) {
return loadKnownPlugins().stream()
.filter(p -> name.equalsIgnoreCase(p.getString("name")))
.findFirst();
}

public static void enable(PluginType pluginType) {
JsonObject pluginConfig = PluginHelper.getOrCreatePluginConfig();
JsonObject plugins = pluginConfig.getMap("plugins");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,28 @@
*/
public enum PluginType {

KUBERNETES("kubernetes", "kubernetes", "Run Camel applications on Kubernetes", "4.8.0", null),
GENERATE("generate", "generate", "Generate code such as DTOs", "4.8.0", null),
EDIT("edit", "edit", "Edit Camel files with suggestions", "4.12.0", null),
TEST("test", "test", "Manage tests for Camel applications", "4.14.0", null),
ROUTE_PARSER("route-parser", "route-parser", "Parses Java route and dumps route structure", "4.17.0", null),
VALIDATE("validate", "validate", "Validate Camel routes", "4.18.0", null),
TUI("tui", "tui", "Camel Dashboard", "4.20.0", null);
KUBERNETES("kubernetes", "kubernetes", "Run Camel applications on Kubernetes", "4.8.0", null, "ASF"),
GENERATE("generate", "generate", "Generate code such as DTOs", "4.8.0", null, "ASF"),
EDIT("edit", "edit", "Edit Camel files with suggestions", "4.12.0", null, "ASF"),
TEST("test", "test", "Manage tests for Camel applications", "4.14.0", null, "ASF"),
ROUTE_PARSER("route-parser", "route-parser", "Parses Java route and dumps route structure", "4.17.0", null, "ASF"),
VALIDATE("validate", "validate", "Validate Camel routes", "4.18.0", null, "ASF"),
TUI("tui", "tui", "Camel Dashboard", "4.20.0", null, "ASF");

private final String name;
private final String command;
private final String description;
private final String firstVersion;
private final String repos;
private final String vendor;

PluginType(String name, String command, String description, String firstVersion, String repos) {
PluginType(String name, String command, String description, String firstVersion, String repos, String vendor) {
this.name = name;
this.command = command;
this.description = description;
this.firstVersion = firstVersion;
this.repos = repos;
this.vendor = vendor;
}

public static Optional<PluginType> findByName(String name) {
Expand Down Expand Up @@ -71,4 +73,8 @@ public String getFirstVersion() {
public String getRepos() {
return repos;
}

public String getVendor() {
return vendor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"name": "forage",
"command": "forage",
"description": "Configure components via opinionated bean factories",
"firstVersion": "4.10.0",
"groupId": "io.kaoto.forage",
"artifactId": "camel-jbang-plugin-forage",
"vendor": "Community"
},
{
"name": "camel-kit",
"command": "kit",
"description": "Design Apache Camel Integrations with AI",
"firstVersion": "4.10.0",
"groupId": "io.github.luigidemasi",
"artifactId": "camel-kit-jbang-plugin",
"vendor": "Community"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ public void shouldGetPlugin() throws Exception {

List<String> output = printer.getLines();
Assertions.assertEquals(2, output.size());
Assertions.assertEquals("NAME COMMAND DEPENDENCY DESCRIPTION",
Assertions.assertEquals("NAME COMMAND VENDOR DEPENDENCY DESCRIPTION",
output.get(0));
Assertions.assertEquals(
"kubernetes kubernetes org.apache.camel:camel-jbang-plugin-kubernetes %s"
"kubernetes kubernetes ASF org.apache.camel:camel-jbang-plugin-kubernetes %s"
.formatted(PluginType.KUBERNETES.getDescription()),
output.get(1));
}
Expand All @@ -70,12 +70,13 @@ public void shouldGetDefaultPlugins() throws Exception {
command.doCall();

List<String> output = printer.getLines();
Assertions.assertEquals(10, output.size());
Assertions.assertTrue(output.size() >= 10);
Assertions.assertEquals("Supported plugins:", output.get(0));
Assertions.assertEquals("NAME COMMAND DEPENDENCY DESCRIPTION",
Assertions.assertEquals(
"NAME COMMAND VENDOR DEPENDENCY DESCRIPTION",
output.get(2));
Assertions.assertEquals(
"kubernetes kubernetes org.apache.camel:camel-jbang-plugin-kubernetes %s"
"kubernetes kubernetes ASF org.apache.camel:camel-jbang-plugin-kubernetes %s"
.formatted(PluginType.KUBERNETES.getDescription()),
output.get(3));
}
Expand All @@ -97,8 +98,9 @@ public void shouldGenerateDependencyAndDescription() throws Exception {

List<String> output = printer.getLines();
Assertions.assertEquals(2, output.size());
Assertions.assertEquals("NAME COMMAND DEPENDENCY DESCRIPTION", output.get(0));
Assertions.assertEquals("foo foo org.apache.camel:camel-jbang-plugin-foo Plugin foo called with command foo",
Assertions.assertEquals("NAME COMMAND VENDOR DEPENDENCY DESCRIPTION", output.get(0));
Assertions.assertEquals(
"foo foo org.apache.camel:camel-jbang-plugin-foo Plugin foo called with command foo",
output.get(1));
}

Expand All @@ -120,43 +122,46 @@ public void shouldGetAllPlugins() throws Exception {
command.doCall();

List<String> output = printer.getLines();
Assertions.assertEquals(13, output.size());
Assertions.assertEquals("NAME COMMAND DEPENDENCY DESCRIPTION", output.get(0));
Assertions.assertTrue(output.size() >= 13);
Assertions.assertEquals("NAME COMMAND VENDOR DEPENDENCY DESCRIPTION", output.get(0));
Assertions.assertEquals(
"foo-plugin foo org.apache.camel:foo-plugin:1.0.0 Plugin foo-plugin called with command foo",
"foo-plugin foo org.apache.camel:foo-plugin:1.0.0 Plugin foo-plugin called with command foo",
output.get(1));

Assertions.assertEquals("Supported plugins:", output.get(3));
Assertions.assertEquals("NAME COMMAND DEPENDENCY DESCRIPTION",
Assertions.assertEquals(
"NAME COMMAND VENDOR DEPENDENCY DESCRIPTION",
output.get(5));
Assertions.assertEquals(
"kubernetes kubernetes org.apache.camel:camel-jbang-plugin-kubernetes %s"
"kubernetes kubernetes ASF org.apache.camel:camel-jbang-plugin-kubernetes %s"
.formatted(PluginType.KUBERNETES.getDescription()),
output.get(6));
Assertions.assertEquals(
"generate generate org.apache.camel:camel-jbang-plugin-generate %s"
"generate generate ASF org.apache.camel:camel-jbang-plugin-generate %s"
.formatted(PluginType.GENERATE.getDescription()),
output.get(7));
Assertions.assertEquals(
"edit edit org.apache.camel:camel-jbang-plugin-edit %s"
"edit edit ASF org.apache.camel:camel-jbang-plugin-edit %s"
.formatted(PluginType.EDIT.getDescription()),
output.get(8));
Assertions.assertEquals(
"test test org.apache.camel:camel-jbang-plugin-test %s"
"test test ASF org.apache.camel:camel-jbang-plugin-test %s"
.formatted(PluginType.TEST.getDescription()),
output.get(9));
Assertions.assertEquals(
"route-parser route-parser org.apache.camel:camel-jbang-plugin-route-parser %s"
"route-parser route-parser ASF org.apache.camel:camel-jbang-plugin-route-parser %s"
.formatted(PluginType.ROUTE_PARSER.getDescription()),
output.get(10));
Assertions.assertEquals(
"validate validate org.apache.camel:camel-jbang-plugin-validate %s"
"validate validate ASF org.apache.camel:camel-jbang-plugin-validate %s"
.formatted(PluginType.VALIDATE.getDescription()),
output.get(11));
Assertions.assertEquals(
"tui tui org.apache.camel:camel-jbang-plugin-tui %s"
"tui tui ASF org.apache.camel:camel-jbang-plugin-tui %s"
.formatted(PluginType.TUI.getDescription()),
output.get(12));

Assertions.assertEquals("Known 3rd party plugins:", output.get(14));
}

}
Loading