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 2023/12/16 08:07:34 UTC
(camel) branch main updated: CAMEL-18082: camel-jbang - Camel run in prompt mode to ask for requir… (#12461)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 2c8a2741771 CAMEL-18082: camel-jbang - Camel run in prompt mode to ask for requir… (#12461)
2c8a2741771 is described below
commit 2c8a27417712aceb9050e6c49d3ed91a8ad22a68
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Dec 16 09:07:27 2023 +0100
CAMEL-18082: camel-jbang - Camel run in prompt mode to ask for requir… (#12461)
* CAMEL-18082: camel-jbang - Camel run in prompt mode to ask for required values. Adjust properties component API to allow access to default value in custom properties lookup and sources.
* CAMEL-18082: camel-jbang - Camel run in prompt mode to ask for required values. Adjust properties component API to allow access to default value in custom properties lookup and sources.
---
.../spi/BridgePropertyPlaceholderConfigurer.java | 2 +-
.../org/apache/camel/spi/PropertiesSource.java | 11 ++++
.../properties/DefaultPropertiesLookup.java | 21 ++++----
.../properties/DefaultPropertiesParser.java | 20 ++++----
.../component/properties/PropertiesLookup.java | 7 +--
.../ROOT/pages/camel-4x-upgrade-guide-4_4.adoc | 2 +
.../modules/ROOT/pages/camel-jbang.adoc | 59 ++++++++++++++++++++++
.../apache/camel/dsl/jbang/core/commands/Run.java | 7 +++
.../java/org/apache/camel/main/KameletMain.java | 6 +++
.../download/PromptPropertyPlaceholderSource.java | 55 ++++++++++++++++++++
10 files changed, 166 insertions(+), 24 deletions(-)
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/BridgePropertyPlaceholderConfigurer.java b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/BridgePropertyPlaceholderConfigurer.java
index 753c35d348f..53c68e96509 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/BridgePropertyPlaceholderConfigurer.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/BridgePropertyPlaceholderConfigurer.java
@@ -180,7 +180,7 @@ public class BridgePropertyPlaceholderConfigurer extends PropertyPlaceholderConf
propVal = resolveSystemProperty(placeholderName);
}
if (propVal == null) {
- propVal = properties.lookup(placeholderName);
+ propVal = properties.lookup(placeholderName, null);
}
if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
propVal = resolveSystemProperty(placeholderName);
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesSource.java b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesSource.java
index 7daa12ad236..0e0a6ff3db0 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesSource.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/PropertiesSource.java
@@ -39,4 +39,15 @@ public interface PropertiesSource {
*/
String getProperty(String name);
+ /**
+ * Gets the property with the name
+ *
+ * @param name name of property
+ * @param defaultValue default value to use as fallback
+ * @return the property value, or <tt>null</tt> if no property exists
+ */
+ default String getProperty(String name, String defaultValue) {
+ return getProperty(name);
+ }
+
}
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
index adf7602be79..629584e0e03 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesLookup.java
@@ -38,15 +38,15 @@ public class DefaultPropertiesLookup implements PropertiesLookup {
}
@Override
- public String lookup(String name) {
+ public String lookup(String name, String defaultValue) {
try {
- return doLookup(name);
+ return doLookup(name, defaultValue);
} catch (NoTypeConversionAvailableException e) {
throw RuntimeCamelException.wrapRuntimeCamelException(e);
}
}
- private String doLookup(String name) throws NoTypeConversionAvailableException {
+ private String doLookup(String name, String defaultValue) throws NoTypeConversionAvailableException {
String answer = null;
// local takes precedence
@@ -57,14 +57,15 @@ public class DefaultPropertiesLookup implements PropertiesLookup {
if (value != null) {
answer = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
String loc = location(local, name, "LocalProperties");
- String defaultValue = null;
+ String localDefaultValue = null;
if (local instanceof OrderedLocationProperties) {
Object val = ((OrderedLocationProperties) local).getDefaultValue(name);
if (val != null) {
- defaultValue = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, val);
+ localDefaultValue
+ = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, val);
}
}
- onLookup(name, answer, defaultValue, loc);
+ onLookup(name, answer, localDefaultValue, loc);
}
}
@@ -75,13 +76,13 @@ public class DefaultPropertiesLookup implements PropertiesLookup {
if (value != null) {
answer = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
String loc = location(local, name, "OverrideProperties");
- onLookup(name, answer, null, loc);
+ onLookup(name, answer, defaultValue, loc);
}
}
if (answer == null) {
// try till first found source
for (PropertiesSource ps : component.getPropertiesSources()) {
- answer = ps.getProperty(name);
+ answer = ps.getProperty(name, defaultValue);
if (answer != null) {
String source = ps.getName();
if (ps instanceof ClasspathPropertiesSource) {
@@ -99,7 +100,7 @@ public class DefaultPropertiesLookup implements PropertiesLookup {
source = olp.getLocation(name);
}
}
- onLookup(name, answer, null, source);
+ onLookup(name, answer, defaultValue, source);
break;
}
}
@@ -111,7 +112,7 @@ public class DefaultPropertiesLookup implements PropertiesLookup {
if (value != null) {
answer = component.getCamelContext().getTypeConverter().mandatoryConvertTo(String.class, value);
String loc = location(local, name, "InitialProperties");
- onLookup(name, answer, null, loc);
+ onLookup(name, answer, defaultValue, loc);
}
}
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
index e74100846fe..4d920df356d 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/DefaultPropertiesParser.java
@@ -355,7 +355,7 @@ public class DefaultPropertiesParser implements PropertiesParser {
key = key.substring(OPTIONAL_TOKEN.length());
}
- String value = doGetPropertyValue(key);
+ String value = doGetPropertyValue(key, defaultValue);
if (value == null && defaultValue != null) {
log.debug("Property with key [{}] not found, using default value: {}", key, defaultValue);
value = defaultValue;
@@ -390,7 +390,7 @@ public class DefaultPropertiesParser implements PropertiesParser {
* @param key Key of the property
* @return Value of the property or {@code null} if not found
*/
- private String doGetPropertyValue(String key) {
+ private String doGetPropertyValue(String key, String defaultValue) {
if (ObjectHelper.isEmpty(key)) {
return parseProperty(key, null, properties);
}
@@ -402,16 +402,16 @@ public class DefaultPropertiesParser implements PropertiesParser {
if (local != null) {
value = local.getProperty(key);
if (value != null) {
- String defaultValue = null;
+ String localDefaultValue = null;
String loc = location(local, key, "LocalProperties");
if (local instanceof OrderedLocationProperties) {
Object val = ((OrderedLocationProperties) local).getDefaultValue(key);
if (val != null) {
- defaultValue
+ localDefaultValue
= propertiesComponent.getCamelContext().getTypeConverter().tryConvertTo(String.class, val);
}
}
- onLookup(key, value, defaultValue, loc);
+ onLookup(key, value, localDefaultValue, loc);
log.debug("Found local property: {} with value: {} to be used.", key, value);
}
}
@@ -427,20 +427,20 @@ public class DefaultPropertiesParser implements PropertiesParser {
if (value == null && envMode == PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_OVERRIDE) {
value = lookupEnvironmentVariable(key);
if (value != null) {
- onLookup(key, value, null, "ENV");
+ onLookup(key, value, defaultValue, "ENV");
log.debug("Found an OS environment property: {} with value: {} to be used.", key, value);
}
}
if (value == null && sysMode == PropertiesComponent.SYSTEM_PROPERTIES_MODE_OVERRIDE) {
value = System.getProperty(key);
if (value != null) {
- onLookup(key, value, null, "SYS");
+ onLookup(key, value, defaultValue, "SYS");
log.debug("Found a JVM system property: {} with value: {} to be used.", key, value);
}
}
if (value == null && properties != null) {
- value = properties.lookup(key);
+ value = properties.lookup(key, defaultValue);
if (value != null) {
log.debug("Found property: {} with value: {} to be used.", key, value);
}
@@ -457,14 +457,14 @@ public class DefaultPropertiesParser implements PropertiesParser {
if (value == null && envMode == PropertiesComponent.ENVIRONMENT_VARIABLES_MODE_FALLBACK) {
value = lookupEnvironmentVariable(key);
if (value != null) {
- onLookup(key, value, null, "ENV");
+ onLookup(key, value, defaultValue, "ENV");
log.debug("Found an OS environment property: {} with value: {} to be used.", key, value);
}
}
if (value == null && sysMode == PropertiesComponent.SYSTEM_PROPERTIES_MODE_FALLBACK) {
value = System.getProperty(key);
if (value != null) {
- onLookup(key, value, null, "SYS");
+ onLookup(key, value, defaultValue, "SYS");
log.debug("Found a JVM system property: {} with value: {} to be used.", key, value);
}
}
diff --git a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesLookup.java b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesLookup.java
index ed629f84bdd..0c8aa15350d 100644
--- a/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesLookup.java
+++ b/core/camel-base/src/main/java/org/apache/camel/component/properties/PropertiesLookup.java
@@ -25,9 +25,10 @@ public interface PropertiesLookup {
/**
* Lookup the property with the given name
*
- * @param name property name
- * @return the property value, or <tt>null</tt> if the properties does not exist.
+ * @param name property name
+ * @param defaultValue default value for the property (if any exists)
+ * @return the property value, or <tt>null</tt> if the properties does not exist.
*/
- String lookup(String name);
+ String lookup(String name, String defaultValue);
}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
index 050676bb9de..51cdc9b460a 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_4.adoc
@@ -13,6 +13,8 @@ use the default constructor if necessary.
The method `getCreated` is now deprecated. Access to the time-related information from the exchange should be done via `getClock`.
+The `lookup` method in `org.apache.camel.component.properties.PropertiesLookup` now has a 2nd parameter for the default value.
+
=== camel-azure-cosmosdb
The useDefaultIdentity parameter has been removed in favor of the credentialType parameter. Now user should select between SHARED_ACCOUNT_KEY and AZURE_IDENTITY.
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index fc9e5a96e25..6fb9d21c388 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -209,6 +209,65 @@ Camel JBang will then scan in `src/main/java` and `src/main/resources` for files
NOTE: Using `camel run pom.xml` is not intended as a fully compatible way of running an existing Maven based project.
+=== Running Route with user interactive prompt for placeholder values
+
+You can create Camel integrations that makes it possible for the user to quickly enter placeholder values from command prompt.
+
+For example given the following route:
+
+[source,java]
+----
+import org.apache.camel.builder.RouteBuilder;
+
+public class foo extends RouteBuilder {
+
+ @Override
+ public void configure() throws Exception {
+ from("timer:java?period={{time:1000}}")
+ .setBody()
+ .simple("Hello Camel from {{you}}")
+ .log("${body}");
+ }
+}
+----
+
+Then if you run this with:
+
+[source,bash]
+----
+camel run foo.java
+----
+
+You will have an exception on startup about the missing value
+`Caused by: java.lang.IllegalArgumentException: Property with key [you] not found in properties from text: Hello Camel from {{you}}`
+
+However, you can then run in prompt mode as follows:
+
+[source,bash]
+----
+camel run foo.java --prompt
+----
+
+And Camel will now prompt in the terminal for you to enter values for the placeholders:
+
+[source,bash]
+----
+2023-12-15 21:46:44.218 INFO 15033 --- [ main] org.apache.camel.main.MainSupport : Apache Camel (JBang) 4.3.0-SNAPSHOT is starting
+2023-12-15 21:46:44.331 INFO 15033 --- [ main] org.apache.camel.main.MainSupport : Using Java 17.0.5 with PID 15033. Started by davsclaus in /Users/davsclaus/workspace/deleteme/prompt
+2023-12-15 21:46:45.360 INFO 15033 --- [ main] mel.cli.connector.LocalCliConnector : Management from Camel JBang enabled
+Enter optional value for time (1000):
+Enter required value for you: Jack
+2023-12-15 21:46:55.239 INFO 15033 --- [ main] el.impl.engine.AbstractCamelContext : Apache Camel 4.3.0-SNAPSHOT (foo) is starting
+2023-12-15 21:46:55.323 INFO 15033 --- [ main] g.apache.camel.main.BaseMainSupport : Property-placeholders summary
+2023-12-15 21:46:55.323 INFO 15033 --- [ main] g.apache.camel.main.BaseMainSupport : [prompt] you=Jack
+2023-12-15 21:46:55.341 INFO 15033 --- [ main] el.impl.engine.AbstractCamelContext : Routes startup (started:1)
+----
+
+From the snippet above, Camel JBang had two prompts. First for the `{{time}}` which has a default value of `1000` so you can just press ENTER to accept the default value.
+And for `{{you}}` a value must be entered, and we entered `Jack` in this example.
+
+You may want to use this for Camel prototypes where you want the user to be able to enter custom values quickly.
+Those values can of course be pre-configured in `application.properties` as well.
=== Running Route from input parameter
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 0afec4ba0ae..a58d5c576cf 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -268,6 +268,10 @@ public class Run extends CamelCommand {
description = "Whether to ignore route loading and compilation errors (use this with care!)")
protected boolean ignoreLoadingError;
+ @Option(names = { "--prompt" },
+ description = "Allow user to type in required parameters in prompt if not present in application")
+ boolean prompt;
+
public Run(CamelJBangMain main) {
super(main);
}
@@ -473,6 +477,9 @@ public class Run extends CamelCommand {
if (ignoreLoadingError) {
writeSetting(main, profileProperties, "camel.jbang.ignoreLoadingError", "true");
}
+ if (prompt) {
+ writeSetting(main, profileProperties, "camel.jbang.prompt", "true");
+ }
writeSetting(main, profileProperties, "camel.jbang.compileWorkDir", WORK_DIR + File.separator + "compile");
if (gav != null) {
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index 2f87e6c2ae4..10ccaae091f 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -59,6 +59,7 @@ import org.apache.camel.main.download.KnownDependenciesResolver;
import org.apache.camel.main.download.KnownReposResolver;
import org.apache.camel.main.download.MavenDependencyDownloader;
import org.apache.camel.main.download.PackageNameSourceLoader;
+import org.apache.camel.main.download.PromptPropertyPlaceholderSource;
import org.apache.camel.main.download.StubBeanRepository;
import org.apache.camel.main.download.TypeConverterLoaderDownloadListener;
import org.apache.camel.main.injection.AnnotationDependencyInjection;
@@ -357,6 +358,11 @@ public class KameletMain extends MainCommandLineSupport {
// setup backlog recorder from very start
answer.getCamelContextExtension().setStartupStepRecorder(new BacklogStartupStepRecorder());
+ boolean prompt = "true".equals(getInitialProperties().get("camel.jbang.prompt"));
+ if (prompt) {
+ answer.getPropertiesComponent().addPropertiesSource(new PromptPropertyPlaceholderSource());
+ }
+
ClassLoader dynamicCL = createApplicationContextClassLoader(answer);
answer.setApplicationContextClassLoader(dynamicCL);
PluginHelper.getPackageScanClassResolver(answer).addClassLoader(dynamicCL);
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/PromptPropertyPlaceholderSource.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/PromptPropertyPlaceholderSource.java
new file mode 100644
index 00000000000..1ff11a89c4e
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/PromptPropertyPlaceholderSource.java
@@ -0,0 +1,55 @@
+/*
+ * 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.download;
+
+import org.apache.camel.Ordered;
+import org.apache.camel.spi.PropertiesSource;
+
+public class PromptPropertyPlaceholderSource implements PropertiesSource, Ordered {
+
+ @Override
+ public String getName() {
+ return "prompt";
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return null; // not in use
+ }
+
+ @Override
+ public String getProperty(String name, String defaultValue) {
+ String answer;
+ if (defaultValue != null) {
+ answer = System.console().readLine("Enter optional value for %s (%s): ", name, defaultValue);
+ } else {
+ do {
+ answer = System.console().readLine("Enter required value for %s: ", name);
+ } while (answer == null || answer.isBlank());
+ }
+ // if user press enter then the value should use the default value
+ if (answer == null || answer.isBlank()) {
+ answer = defaultValue;
+ }
+ return answer;
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST;
+ }
+}