This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-23806-split-user-manual-docs in repository https://gitbox.apache.org/repos/asf/camel.git
commit 58f3191d35ffd2e7b9c7a0148372f673145c0226 Author: Claus Ibsen <[email protected]> AuthorDate: Sat Jun 20 17:42:48 2026 +0200 CAMEL-23806: Split and condense user manual docs - Extract property placeholder functions into sub-page - Extract route template bean binding into sub-page - Condense security.adoc vault sections by showing usage syntax once Co-Authored-By: Claude <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- docs/user-manual/modules/ROOT/nav.adoc | 2 + ...er.adoc => property-placeholder-functions.adoc} | 747 +--------- .../ROOT/pages/route-template-bean-binding.adoc | 641 ++++++++ .../modules/ROOT/pages/route-template.adoc | 639 +------- docs/user-manual/modules/ROOT/pages/security.adoc | 1557 +++----------------- .../ROOT/pages/using-propertyplaceholder.adoc | 741 +--------- 6 files changed, 822 insertions(+), 3505 deletions(-) diff --git a/docs/user-manual/modules/ROOT/nav.adoc b/docs/user-manual/modules/ROOT/nav.adoc index 76239ecd48a6..b3d74e9d0d06 100644 --- a/docs/user-manual/modules/ROOT/nav.adoc +++ b/docs/user-manual/modules/ROOT/nav.adoc @@ -27,6 +27,7 @@ ** xref:error-handler.adoc[Error handler] ** xref:error-registry.adoc[Error Registry] ** xref:using-propertyplaceholder.adoc[How to use Camel property placeholders] +*** xref:property-placeholder-functions.adoc[Property Placeholder Functions] ** xref:variables.adoc[How to use Variables] ** xref:testing.adoc[Testing] *** xref:test-infra.adoc[Test Infra] @@ -97,6 +98,7 @@ ** xref:route-reload.adoc[RouteReload] ** xref:route-diagram.adoc[Visual Route Diagrams] ** xref:route-template.adoc[RouteTemplate] +*** xref:route-template-bean-binding.adoc[Bean Binding] ** xref:routes.adoc[Routes] ** xref:startup-condition.adoc[Startup Condition] ** xref:stream-caching.adoc[Stream caching] diff --git a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc b/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc similarity index 50% copy from docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc copy to docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc index 28e6aa917433..20521888302a 100644 --- a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc +++ b/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc @@ -1,718 +1,7 @@ -= Property placeholders += Property Placeholder Functions +:tabs-sync-option: -Camel has extensive support for property placeholders, which -can be used _almost anywhere_ in your Camel xref:routes.adoc[routes], -xref:endpoint.adoc[endpoints], xref:dsl.adoc[DSL], and xref:route-configuration.adoc[route configuration], -xref:bean-integration.adoc[bean integration] and elsewhere. - -Property placeholders are used to define a _placeholder_ instead of the actual value. -This is important as you would want to be able to make your applications external -configurable, such as values for network addresses, port numbers, authentication credentials, -login tokens, and configuration in general. - -== Properties component - -Camel provides the xref:components::properties-component.adoc[Properties] out of the box -from the core, which is responsible for handling and resolving the property placeholders. - -See the xref:components::properties-component.adoc[Properties] documentation for how -to configure Camel to known from which location(a) to load properties. - -== Property placeholder syntax - -The value of a Camel property can be obtained by specifying its key name -within a property placeholder, using the following syntax: `{\{key}}` - -For example: - -[source,text] ----- -{{file.uri}} ----- - -where `file.uri` is the property key. - -Property placeholders can for example be used to specify parts, or all, of an -xref:uris.adoc[endpoint uri] by embedding one or more placeholders in the URI's string -definition. - -For example as shown in the route below: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("{{file.uri}}") - .to("kafka:{{orderTopic}}"); ----- - -XML:: -+ -[source,xml] ----- - <route> - <from uri="{{file.uri}}"/> - <to uri="kafka:{{orderTopic}}"/> - </route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: "{{file.uri}}" - steps: - - to: - uri: "kafka:{{orderTopic}}" ----- -==== - -Then the placeholder can be declared in `application.properties`: - -[source,properties] ----- -file.uri = file:/var/orders/inbox?recursive=true ----- - - -=== Using property placeholder with default value - -You can specify a default value to use if a -property with the key does not exist, where the default value is the text after the colon: - -[source,text] ----- -{{file.url:/some/path}} ----- - -In this case the default value is `/some/path`. - -=== Using optional property placeholders - -Camel's elaborate property placeholder feature supports optional placeholders, -which is declared with the `?` (question mark) as prefix in the key name, as shown: - -[source,text] ----- -{{?myBufferSize}} ----- - -If a value for the key exists then the value is used, however if the key does not exist, -then Camel understands how to handle this. For example when used in xref:endpoint.adoc[Endpoints]: - -[source,text] ----- -file:foo?bufferSize={{?myBufferSize}} ----- - -Then the `bufferSize` option will only be configured in the endpoint, if a placeholder exists. -Otherwise, the option will not be set on the endpoint, meaning the endpoint would be _restructured_ as: - -[source,text] ----- -file:foo ----- - -Then the option `bufferSize` is not declared, and this would allow Camel to -use the standard default value for `bufferSize` (if any exists). - -=== Reverse a boolean value - -If a property placeholder is a boolean value, then it is possible to negate (reverse) the value by using `!` as prefix in the key. - -For example given we have this property: - -[source,properties] ----- -integration.ftpEnabled=true ----- - -Then this can be used to control which routes should be auto started. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("ftp:....").autoStartup("{{integration.ftpEnabled}}") - .to("kafka:cheese"); - -from("jms:....").autoStartup("{{!integration.ftpEnabled}}") - .to("kafka:cheese"); ----- - -XML:: -+ -[source,xml] ----- -<route autoStartup="{{integration.ftpEnabled}}"> - <from uri="ftp:...."/> - <to uri="kafka:cheese"/> -</route> - -<route autoStartup="{{!integration.ftpEnabled}}"> - <from uri="jms:...."/> - <to uri="kafka:cheese"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - autoStartup: "{{integration.ftpEnabled}}" - from: - uri: ftp:.... - steps: - - to: - uri: kafka:cheese -- route: - autoStartup: "{{!integration.ftpEnabled}}" - from: - uri: jms:.... - steps: - - to: - uri: kafka:cheese ----- -==== - -In the example above then the FTP route or the JMS route should only be started. So if the FTP is enabled then JMS should be disabled, and vice versa. -We can do this be negating the `autoStartup` in the JMS route, by using `!integration.ftpEnabled` as the key. - - -== Using property placeholders - -When using property placeholders in the endpoint xref:uris.adoc[URIs] you should use this with the syntax `{\{key}}` as shown in this example: - -[source,properties] ----- -cool.end = mock:result -where = cheese ----- - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cool.end}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cool.end}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cool.end}}" ----- -==== - -A property placeholder may also just be a one part in the endpoint URI. - -A common use-case is to use a placeholder for an endpoint option such -as the size of the write buffer in the file endpoint: - -[source,properties] ----- -buf = 8192 ----- - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("file:outbox?bufferSize={{buf}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="file:outbox?bufferSize={{buf}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: file:outbox - parameters: - bufferSize: "{{buf}}" ----- -==== - -However, the placeholder can be anywhere, so it could also be the name of a mock endpoint - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("mock:{{where}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="mock:{{where}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "mock:{{where}}" ----- -==== - -In the example above the mock endpoint, is already hardcoded to start with `mock:`, -and the `where` placeholder has the value `cheese` so the resolved uri becomes `mock:cheese`. - -=== Property placeholders referring to other properties (nested placeholders) - -You can also have properties with refer to each other such as: - -[source,properties] ----- -cool.foo=result -cool.concat=mock:{{cool.foo}} ----- - -Notice how `cool.concat` refer to another property (above). - -You can then use the `cool.concat` placeholder in the Camel routes: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cool.concat}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cool.concat}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cool.concat}}" ----- -==== - - -==== Turning off nested placeholders (advanced) - -If the placeholder value contains data that interfere with the property placeholder syntax `{{` and `}}` (such as JSon data), -you can be then explicit turn off nested placeholder by `?nested=false` in the key name, such as shown: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("elasticsearch:foo?query={{myQuery?nested=false}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="elasticsearch:foo?query={{myQuery?nested=false}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: elasticsearch:foo - parameters: - query: "{{myQuery?nested=false}}" ----- -==== - -In the example above the placeholder _myQuery_ placeholder value is as follows - -[source,properties] ----- -myQuery = {"query":{"match_all":{}}} ----- - -Notice how the JSon query ends with `}}` which interfere with the Camel property placeholder syntax. - -Nested placeholders can also be turned off globally on the xref:components::properties-component.adoc[Properties] component, such as: - -[source,java] ----- -CamelContext context = ... -context.getPropertiesComponent().setNestedPlaceholder(false); ----- - -In the situation with the JSon query ending with `}}` which clashes with Camels property placeholder syntax, then its also possible -to use escaping as explained in the following section. - -==== Escaping a property placeholder - -The property placeholder can be problematic if the double curly brackets are used by a third party library like for example a query in ElasticSearch of type `{"query":{"match_all":{}}}`. - -To work around that it is possible to escape the double curly brackets with a backslash character like for example `\{{ property-name \}}`. This way, it won't be interpreted as a property placeholder to resolve and will be resolved as `{{ property-name }}`. - -The previous example can then use escaping (see how the value ends with `\\}}`) - -[source,properties] ----- -myQuery = {"query":{"match_all":{}\\}} ----- - -If for some reason, the backslash character before the double curly brackets must not be interpreted as an escape character, it is possible to add another backslash in front of it to escape it, it will then be seen as a backslash. - -=== Using property placeholders multiple times - -You can of course also use placeholders several times: - -[source,properties] ----- -cool.start=direct:start -cool.showid=true -cool.result=result ----- - -And in this route we use `cool.start` two times: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("{{cool.start}}") - .to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}") - .to("mock:{{cool.result}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="{{cool.start}}"/> - <to uri="log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}"/> - <to uri="mock:{{cool.result}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "log:{{cool.start}}" - parameters: - showBodyType: "false" - showExchangeId: "{{cool.showid}}" - - to: - uri: "mock:{{cool.result}}" ----- -==== - -=== Using property placeholders with consumer and producer templates - -You can also your property placeholders when using -xref:producertemplate.adoc[ProducerTemplate] for example: - -[source,java] ----- -template.sendBody("{{cool.start}}", "Hello World"); ----- - -This can also be done when using xref:consumertemplate.adoc[ConsumerTemplate], such as: - -[source,java] ----- -Object body = template.receiveBody("{{cool.start}}"); ----- - -== Resolving property placeholders from Java code - -If you need to resolve property placeholder(s) from some Java code, then Camel has two APIs for this: - -- You can use the method `resolveProperty` on the `PropertiesComponent` to resolve a single property from Java code. -- Use the method `resolvePropertyPlaceholders` on the `CamelContext` to resolve (one or more) property placeholder(s) in a String. - -For example to resolve a placeholder with key foo, you can do: - -[source,java] ----- -Optional<String> prop = camelContext.getPropertiesComponent().resolveProperty("foo"); -if (prop.isPresent()) { - String value = prop.get(); - .... -} ----- - -This API is to lookup a single property and returns a `java.util.Optional` type. - -The `CamelContext` have another API which is capable of resolving multiple placeholders, and interpolate placeholders from an input String. -Let's try with an example to explain this: - -[source,java] ----- -String msg = camelContext.resolvePropertyPlaceholders("{{greeting}} Camel user, Camel is {{cool}} dont you think?"); ----- - -The input string is a text statement which have two placeholders that will be resolved, for example: - -[source,properties] ----- -greeting = Hi -cool = awesome ----- - -Will be resolved to: - -[source,text] ----- -Hi Camel user, Camel is awesome dont you think? ----- - -== Resolving property placeholders on cloud - -When you are running your Camel application on the cloud you may want to automatically scan any Configmap or Secret as it was an application properties. Given the following Secret: - -[source,yaml] ----- -apiVersion: v1 -data: - my-property: Q2FtZWwgNC44 -kind: Secret -metadata: - name: my-secret -type: Opaque ----- - -You can mount it in your Pod container, for instance, under `/etc/camel/conf.d/_secrets/my-secret`. Now, just make your Camel application be aware where to scan your configuration via `camel.main.cloud-properties-location = /etc/camel/conf.d/_secrets/my-secret` application properties. It's a comma separated value, so, you can add as many Secrets/Configmaps you need. - -At runtime, you will be able to read the configuration transparently as ```{{ my-property }}``` as you're doing with the rest of properties. - -NOTE: the same configuration works with Configmap. - - -== Special features for legacy Spring XML files - -Apache Camel started decade(s) ago when Spring Framework was heavily used with Spring XML files. -This means Camel has special features that apply only to Spring XML. - -=== Using property placeholders for any kind of attribute in Spring XML files - -Previously it was only the `xs:string` type attributes in the XML DSL -that support placeholders. For example often a timeout attribute would -be a `xs:int` type, and thus you cannot set a string value as the -placeholder key. This is now possible using a special -placeholder namespace. - -In the example below we use the `prop` prefix for the namespace -`\http://camel.apache.org/schema/placeholder`. Now we can use `prop:` as prefix -to configure any kind of XML attributes in Spring XML files. - -In the example below we want to use a placeholder for the `stopOnException` option in -the xref:components:eips:multicast-eip.adoc[Multicast] EIP. The `stopOnException` is a `xs:boolean` type, -so we cannot configure this as: - -[source,xml] ----- -<multicast stopOnException="{{stop}}"> - ... -</multicast> ----- - -Instead, we must use the `prop:` namespace, so we must add this namespace -in the top of the XML file in the `<beans>` tag. - -To configure the option we must then use the `prop:optionName` as shown below: - -[source,xml] ----- -<multicast prop:stopOnException="stop"> - ... -</multicast> ----- - -The complete example is below: - -.Spring XML -[source,xml] ----- -<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:prop="http://camel.apache.org/schema/placeholder" - xsi:schemaLocation=" - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd - http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd"> - - <bean id="damn" class="java.lang.IllegalArgumentException"> - <constructor-arg index="0" value="Damn"/> - </bean> - - <camelContext xmlns="http://camel.apache.org/schema/spring"> - <propertyPlaceholder id="properties" location="classpath:myprop.properties"/> - <route> - <from uri="direct:start"/> - <!-- use prop namespace, to define a property placeholder, which maps to option stopOnException={{stop}} --> - <multicast prop:stopOnException="stop"> - <to uri="mock:a"/> - <throwException ref="damn"/> - <to uri="mock:b"/> - </multicast> - </route> - </camelContext> -</beans> ----- - -In our properties file we have the value defined as: - -[source,properties] ----- -stop = true ----- - -=== Bridging Camel property placeholders with Spring XML files - -NOTE: If you are using Spring Boot then this does not apply. -This is only for legacy Camel and Spring applications which are using Spring XML files. - -The Spring Framework does not allow third-party frameworks such as -Apache Camel to seamless hook into the Spring property placeholder -mechanism. However, you can bridge Spring and Camel by declaring a -Spring bean with the type -`org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer`, -which is a Spring -`org.springframework.beans.factory.config.PropertyPlaceholderConfigurer` -type. - -To bridge Spring and Camel you must define a single bean as shown below: - -.Spring XML -[source,xml] ----- -<!-- bridge spring property placeholder with Camel --> -<!-- you must NOT use the <context:property-placeholder at the same time, only this bridge bean --> -<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer"> - <property name="location" value="classpath:org/apache/camel/component/properties/cheese.properties"/> -</bean> ----- - -You *must not* use the spring `<context:property-placeholder>` namespace -at the same time; this is not possible. - -After declaring this bean, you can define property placeholders using -both the Spring style, and the Camel style within the `<camelContext>` -tag as shown below: - -.Spring XML -[source,xml] ----- -<!-- a bean that uses Spring property placeholder --> -<!-- the ${hi} is a spring property placeholder --> -<bean id="hello" class="org.apache.camel.component.properties.HelloBean"> - <property name="greeting" value="${hi}"/> -</bean> - -<camelContext xmlns="http://camel.apache.org/schema/spring"> - <!-- in this route we use Camels property placeholder {{ }} style --> - <route> - <from uri="direct:{{cool.bar}}"/> - <bean ref="hello"/> - <to uri="{{cool.end}}"/> - </route> -</camelContext> ----- - -Notice how the hello bean is using pure Spring property placeholders using -the `${}` notation. And in the Camel routes we use the Camel -placeholder notation with `{\{key}}`. - - -== Using property placeholder functions +xref:using-propertyplaceholder.adoc[Back to Property Placeholders] The xref:components::properties-component.adoc[Properties] component includes the following functions out of the box: @@ -1010,7 +299,7 @@ YAML:: IMPORTANT: The method must be a public no-arg method (i.e. no parameters) and return a value such as a String, boolean, int. -=== Using Kubernetes property placeholder functions +== Using Kubernetes property placeholder functions The `camel-kubernetes` component include the following functions: @@ -1372,7 +661,7 @@ NOTE: Notice how the key is prefixed with the name of the secret and a slash, eg from the actual use of the configmap/secret and into the `application.properties` file. -=== Using custom property placeholder functions +== Using custom property placeholder functions The xref:components::properties-component.adoc[Properties] component allow to plugin 3rd party functions which can be used during parsing of the property placeholders. These functions are then able to do custom logic to resolve the placeholders, such as looking up in databases, do custom computations, or whatnot. @@ -1452,29 +741,3 @@ and have this logic in `doStart` and `doStop` methods. TIP: For an example see the `camel-base64` component. -== Using third party property sources - -The properties component allows to plugin 3rd party sources to load and lookup properties via the `PropertySource` -API from camel-api. - -The regular `PropertySource` will lookup the property on-demand, -for example to lookup values from a backend source such as a database or HashiCorp Vault etc. - -A `PropertySource` can define that it supports loading all its properties -(by implementing `LoadablePropertiesSource`) from the source at once, for example from file system. -This allows Camel properties component to load these properties at once during startup. - -For example the `camel-microprofile-config` component is implemented using this. -The 3rd-party `PropertySource` can automatically be discovered from classpath when Camel is starting up. -This is done by including the file `META-INF/services/org/apache/camel/property-source-factory` which refers to the fully qualified class name of the `PropertySource` implementation. - -See xref:components:others:microprofile-config.adoc[MicroProfile Config] component as an example. - -You can also register 3rd-party property sources via Java API on the `PropertiesComponent` as shown: - -[source,java] ----- -PropertiesComponent pc = context.getPropertiesComponent(); -pc.addPropertiesSource(myPropertySource); ----- - diff --git a/docs/user-manual/modules/ROOT/pages/route-template-bean-binding.adoc b/docs/user-manual/modules/ROOT/pages/route-template-bean-binding.adoc new file mode 100644 index 000000000000..bee62f0330af --- /dev/null +++ b/docs/user-manual/modules/ROOT/pages/route-template-bean-binding.adoc @@ -0,0 +1,641 @@ += Route Template Bean Binding +:tabs-sync-option: + +xref:route-template.adoc[Back to Route Templates] + +The route template allows binding beans that are locally scoped and only used as part of creating routes from the template. +This allows using the same template to create multiple routes, where beans are local (private) for each created route. + +For example, given the following route template where we use `templateBean` to set up the local bean as shown: + +._Java-only: templateBean with lambda supplier_ +[source,java] +---- +routeTemplate("s3template") + .templateParameter("region") + .templateParameter("bucket") + .templateBean("myClient", S3Client.class, rtc -> + S3Client.builder().region(rtc.getProperty("region", Region.class)).build(); + ) + .from("direct:s3-store") + // must refer to the bean with {{myClient}} + .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") +---- + +The template has two parameters to specify the AWS region and the S3 bucket. To connect to S3 +then a `software.amazon.awssdk.services.s3.S3Client` bean is necessary. + +To create this bean, we specify this with the `templateBean` DSL where we specify the bean id as `myClient`. +The type of the bean can be specified (`S3Client.class`), however, it is optional, and +can be used if you need to let beans be discovered by type and not by name. + +This ensures that the code creating the bean is executed later (when Camel is creating a route from the template), +then the code must be specified as a _supplier_. Because we want during creation of the bean access to template parameters, +we use a Camel `BeanSupplier` which gives access to `RouteTemplateContext` that is the `_rtc_` variable in the code above. + +IMPORTANT: The local bean with id `myClient` *must* be referred to using Camel's property placeholder syntax, eg `{\{myClient}}` +in the route template, as shown above with the _to_ endpoint. This is because the local +bean must be made unique and Camel will internally re-assign the bean id to use a unique id instead of `myClient`. And this is done with the help +of the property placeholder functionality. + +If multiple routes are created from this template, then each of the created routes have their own +`S3Client` bean created. + +== Binding beans to route templates from template builder + +The `TemplatedRouteBuilder` also allows to bind local beans (which allows specifying those beans) when +creating routes from existing templates. + +Suppose the route template below is defined in XML: + +._XML-only: route template without bean binding_ +[source,xml] +---- + <routeTemplate id="s3template"> + <templateParameter name="region"/> + <templateParameter name="bucket"/> + <route> + <from uri="direct:s3-store"/> + <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> + </route> + </routeTemplate> +---- + +The template has no bean bindings for `#{\{myClient}}` which would be required for creating the template. + +When creating routes form the template via `TemplatedRouteBuilder` then you can provide the bean binding +if you desire the bean to be locally scoped (not shared with others): + +[tabs] +==== + +Java:: ++ +[source,java] +---- +TemplatedRouteBuilder.builder(context, "s3template") + .parameter("region", "US-EAST-1") + .parameter("bucket", "myBucket") + .bean("myClient", S3Client.class, + S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build()) + .routeId("mys3route") + .add(); +---- ++ +As you can see the binding is similar to when using `templateBean` directly in the route template. + +Java DSL:: ++ +[source,java] +---- +templatedRoute("s3template") + .parameter("region", "US-EAST-1") + .parameter("bucket", "myBucket") + .bean("myClient", S3Client.class, + rtc -> S3Client.builder() // <1> + .region(rtc.getProperty("region", Region.class)) + .build()) + .routeId("mys3route"); +---- +<1> Note that the third parameter of the `bean` method is not directly the bean but rather a factory method that will be used to create the bean, here we use a lambda expression as factory method. + +XML:: ++ +[source,xml] +---- + <templatedRoute routeTemplateRef="s3template" routeId="mys3route"> + <parameter name="region" value="US-EAST-1"/> + <parameter name="bucket" value="myBucket"/> + <bean name="myClient" type="software.amazon.awssdk.services.s3.S3Client" + scriptLanguage="groovy"> <!--1--> + <script> + import software.amazon.awssdk.services.s3.S3Client + S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build() + </script> + </bean> + </templatedRoute> +---- +<1> For non-Java DSL, in case of a complex bean factory, you can still rely on a language like `groovy` to define your bean factory inside a `script` element. + +YAML:: ++ +[source,yaml] +---- +- templatedRoute: + routeTemplateRef: "s3template" + routeId: "mys3route" + parameters: + - name: "region" + value: "US-EAST-1" + - name: "bucket" + value: "myBucket" + beans: + - name: "myClient" + type: "software.amazon.awssdk.services.s3.S3Client" + scriptLanguage: "groovy" + script: | # <1> + import software.amazon.awssdk.services.s3.S3Client + S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build() +---- +<1> For non-Java DSL, in case of a complex bean factory, you can still rely on a language like `groovy` to define your bean factory as value of the `script` key. +==== + +Instead of binding the beans from the template builder, you could also create the bean outside the template, +and bind it by reference. + +[tabs] +==== + +Java:: ++ +[source,java] +---- +final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build(); + +TemplatedRouteBuilder.builder(context, "s3template") + .parameter("region", Region.US_EAST_1) + .parameter("bucket", "myBucket") + .bean("myClient", myClient) + .routeId("mys3route") + .add(); +---- + +Java DSL:: ++ +[source,java] +---- +final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build(); + +templatedRoute("s3template") + .parameter("region", "US-EAST-1") + .parameter("bucket", "myBucket") + .bean("myClient", S3Client.class, rtc -> myClient) + .routeId("mys3route"); +---- +==== + +NOTE: You should prefer to create the local beans directly from within the template (if possible) because this +ensures the route template has this out of the box. Otherwise, the bean must be created or provided every time +a new route is created from the route template. However, the latter gives freedom to create the bean in any other custom way. + + +== Binding beans to route templates using bean types + +You can create a local bean by referring to a fully qualified class name which Camel will use to create +a new local bean instance. When using this, the created bean is created via default constructor of the class. + +The bean instance can be configured with properties via getter/setter style. +The previous example with creating the AWS S3Client would not support this kind as this uses _fluent builder_ pattern (not getter/setter). + +TIP: In *Camel 4.6* onwards, you can also use constructor arguments for beans + +So suppose we have a class as follows: + +._Java-only: bean class with getter/setter properties_ +[source,java] +---- +public class MyBar { + private String name; + private String address; + + // getter/setter omitted + + public String location() { + return "The bar " + name + " is located at " + address; + } +} +---- + +Then we can use the `MyBar` class as a local bean in a route template as follows: + +[tabs] +==== + +Java:: ++ +[source,java] +---- +routeTemplate("barTemplate") + .templateParameter("bar") + .templateParameter("street") + .templateBean("myBar") + .typeClass("com.foo.MyBar") + .property("name", "{{bar}}") + .property("address", "{{street}}") + .end() + .from("direct:going-out") + .to("bean:{{myBar}}"); +---- ++ +With Java DSL, you can also refer to the bean class using type safe way in `typeClass` by referring to the `.class` as follows: ++ +[source,java] +---- +routeTemplate("barTemplate") + .templateParameter("bar") + .templateParameter("street") + .templateBean("myBar") + .typeClass(MyBar.class) + .property("name", "{{bar}}") + .property("address", "{{street}}") + .end() + .from("direct:going-out") + .to("bean:{{myBar}}"); +---- + +XML:: ++ +In XML, we specify the class FQN name using `#class:` syntax as shown: ++ +[source,xml] +---- +<routeTemplate id="myBar"> + <templateParameter name="bar"/> + <templateParameter name="street"/> + <templateBean name="myBean" type="#class:com.foo.MyBar"> + <properties> + <property key="name" value="{{bar}}"/> + <property key="address" value="{{street}}"/> + </properties> + </templateBean> + <route> + <from uri="direct:going-out"/> + <to uri="bean:{{myBar}}"/> + </route> +</routeTemplate> +---- + +YAML:: ++ +In YAML, we specify the class FQN name using `#class:` syntax as shown: ++ +[source,yaml] +---- +- routeTemplate: + id: "myBar" + parameters: + - name: "bar" + - name: "street" + beans: + - name: "myBean" + type: "#class:com.foo.MyBar" + properties: + name: "{{bar}}" + address: "{{street}}" + from: + uri: direct:going-out + steps: + - to: + uri: "bean:{{myBar}}" +---- +==== + + +== Binding beans to route templates using scripting languages (advanced) + +You can use scripting languages like Groovy, Java, to create the bean. +This allows defining route templates with the scripting language supported by Camel such as Groovy. + +For example, creating the AWS S3 client can be done as shown in Java (with inlined Groovy code): + +[tabs] +==== + +Java:: ++ +[source,java] +---- +routeTemplate("s3template") + .templateParameter("region") + .templateParameter("bucket") + .templateBean("myClient", "groovy", + "software.amazon.awssdk.services.s3.S3Client.S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build()" + ) + .from("direct:s3-store") + // must refer to the bean with {{myClient}} + .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") +---- + +XML:: ++ +Notice how the Groovy code can be inlined directly in the route template in XML also. ++ +[source,xml] +---- + <routeTemplate id="s3template"> + <templateParameter name="region"/> + <templateParameter name="bucket"/> + <templateBean name="myClient" scriptLanguage="groovy"> + <script> + import software.amazon.awssdk.services.s3.S3Client + S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build() + </script> + </templateBean> + <route> + <from uri="direct:s3-store"/> + <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> + </route> + </routeTemplate> +---- + +YAML:: ++ +Notice how the Groovy code can be inlined directly in the route template in DSL also. ++ +[source,yaml] +---- +- routeTemplate: + id: "s3template" + parameters: + - name: "region" + - name: "bucket" + beans: + - name: "myClient" + scriptLanguage: "groovy" + script: | + import software.amazon.awssdk.services.s3.S3Client + S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build() + from: + uri: direct:s3-store + steps: + - to: + uri: "aws2-s3:{{bucket}}" + parameters: + amazonS3Client: "#{{myClient}}" +---- +==== + +The groovy code can be externalized into a file on the classpath or file system, by using `resource:` as prefix, such as: + +[tabs] +==== + +Java:: ++ +[source,java] +---- +routeTemplate("s3template") + .templateParameter("region") + .templateParameter("bucket") + .templateBean("myClient", "groovy", "resource:classpath:s3bean.groovy") + .from("direct:s3-store") + // must refer to the bean with {{myClient}} + .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") +---- + +XML:: ++ +Notice how we can tell Camel the type of the bean is `software.amazon.awssdk.services.s3.S3Client` which allows +Camel to better understand, and also makes the bean able for autowiring and binding via type. The `type` is optional +and can be omitted. For example in this example as we inject the bean via its bean id `myClient`. ++ +[source,xml] +---- + <routeTemplate id="s3template"> + <templateParameter name="region"/> + <templateParameter name="bucket"/> + <templateBean name="myClient" scriptLanguage="groovy" + type="software.amazon.awssdk.services.s3.S3Client"> + <script>resource:classpath:s3bean.groovy</script> + </templateBean> + <route> + <from uri="direct:s3-store"/> + <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> + </route> + </routeTemplate> +---- + +YAML:: ++ +Notice how we can tell Camel the type of the bean is `software.amazon.awssdk.services.s3.S3Client` which allows +Camel to better understand, and also makes the bean able for autowiring and binding via type. The `type` is optional +and can be omitted. For example in this example as we inject the bean via its bean id `myClient`. ++ +[source,yaml] +---- +- routeTemplate: + id: "s3template" + parameters: + - name: "region" + - name: "bucket" + beans: + - name: "myClient" + scriptLanguage: "groovy" + type: "software.amazon.awssdk.services.s3.S3Client" + script: "resource:classpath:s3bean.groovy" + from: + uri: direct:s3-store + steps: + - to: + uri: "aws2-s3:{{bucket}}" + parameters: + amazonS3Client: "#{{myClient}}" +---- +==== + +Then create the file `s3bean.groovy` in the classpath root: + +.s3bean.groovy +[source,groovy] +---- +import software.amazon.awssdk.services.s3.S3Client + +S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build() +---- + +The languages supported you can specify as `scriptLanguage` are: + +[width="100%",cols="2s,8",options="header"] +|=== +| Type | Description +| bean | Calling a method on a Java class to create the bean. +| groovy | Using a groovy script to create the bean. +| java | Java code which is runtime compiled (using jOOR library) to create the bean. +| mvel | To use a MVEL template script to create the bean. +| ognl | To use OGNL template script to create the bean. +| _name_ | To use a third-party language by the given `_name_` to create the bean. +|=== + +Camel will bind `RouteTemplateContext` as the root object with name `rtc` when evaluating the script. +This means you can get access to all the information from `RouteTemplateContext` and `CamelContext` via `rtc`. + +This is what we have done in the scripts in the previous examples where we get hold of a template parameter with: + +[source,groovy] +---- +rtc.getProperty('region', String.class) +---- + +To get access to `CamelContext` you can do: + +[source,groovy] +---- +var cn = rtc.getCamelContext().getName() +---- + +The most powerful languages to use are groovy and java. The other languages are limited in flexibility +as they are not complete programming languages, but are more suited for templating needs. + +It is recommended to either use groovy or java, if creating the local bean requires coding, +and the route templates are not defined using Java code. + +The bean language can be used when creating the local bean from an existing Java method (static or not-static method), +and the route templates are not defined using Java code. + +For example suppose there is a class named `com.foo.MyAwsHelper` that has a method called `createS3Client` +then you can call this method from the route template as shown in the following: + +[tabs] +==== + +Java:: ++ +[source,java] +---- +routeTemplate("s3template") + .templateParameter("region") + .templateParameter("bucket") + .templateBean("myClient", "beam", "com.foo.MyAwsHelper?method=createS3Client") + .from("direct:s3-store") + // must refer to the bean with {{myClient}} + .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") +---- + +XML:: ++ +[source,xml] +---- + <routeTemplate id="s3template"> + <templateParameter name="region"/> + <templateParameter name="bucket"/> + <templateBean name="myClient" type="bean"> + <script>com.foo.MyAwsHelper?method=createS3Client</script> + </templateBean> + <route> + <from uri="direct:s3-store"/> + <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> + </route> + </routeTemplate> +---- + +YAML:: ++ +[source,yaml] +---- +- routeTemplate: + id: "s3template" + parameters: + - name: "region" + - name: "bucket" + beans: + - name: "myClient" + scriptLanguage: "bean" + script: "com.foo.MyAwsHelper?method=createS3Client" + from: + uri: direct:s3-store + steps: + - to: + uri: "aws2-s3:{{bucket}}" + parameters: + amazonS3Client: "#{{myClient}}" +---- +==== + +The method signature of `createS3Client` method **MUST** then have one parameter for the `RouteTemplateContext` as shown: + +._Java-only: bean factory method signature_ +[source,java] +---- +public static S3Client createS3Client(RouteTemplateContext rtc) { + return S3Client.builder() + .region(rtc.getProperty("region", Region.class)) + .build(); +} +---- + +If you are using pure Java code (both template and creating local bean), +then you can create the local bean using Java lambda style as previously documented. + + +== Configuring the type of the created bean + +The `type` must be set to define what FQN class the created bean. + +In the following route template we want to tell Camel that the bean is a `software.amazon.awssdk.services.s3.S3Client` type. + + +[tabs] +==== + +Java:: ++ +In Java DSL you can refer to the type via its class (ie `S3Client.java` which is a type safe way: ++ +[source,java] +---- +routeTemplate("s3template") + .templateParameter("region") + .templateParameter("bucket") + .templateBean("myClient", S3Client.class, "bean", "com.foo.MyAwsHelper?method=createS3Client") + .from("direct:s3-store") + // must refer to the bean with {{myClient}} + .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") +---- + +XML:: ++ +[source,xml] +---- + <routeTemplate id="s3template"> + <templateParameter name="region"/> + <templateParameter name="bucket"/> + <templateBean name="myClient" scriptLanguage="bean" + type="software.amazon.awssdk.services.s3.S3Client"> + <script>com.foo.MyAwsHelper?method=createS3Client</script> + </templateBean> + <route> + <from uri="direct:s3-store"/> + <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> + </route> + </routeTemplate> +---- + +YAML:: ++ +[source,yaml] +---- +- routeTemplate: + id: "s3template" + parameters: + - name: "region" + - name: "bucket" + beans: + - name: "myClient" + scriptLanguage: "bean" + type: "software.amazon.awssdk.services.s3.S3Client" + script: "com.foo.MyAwsHelper?method=createS3Client" + from: + uri: direct:s3-store + steps: + - to: + uri: "aws2-s3:{{bucket}}" + parameters: + amazonS3Client: "#{{myClient}}" +---- +==== + + diff --git a/docs/user-manual/modules/ROOT/pages/route-template.adoc b/docs/user-manual/modules/ROOT/pages/route-template.adoc index 8a6434324929..e06ab30d8d51 100644 --- a/docs/user-manual/modules/ROOT/pages/route-template.adoc +++ b/docs/user-manual/modules/ROOT/pages/route-template.adoc @@ -643,643 +643,12 @@ Then we can create a 2nd route with a different _prefixId`: ---- ==== -== Binding beans to route template (advanced) +== Binding Beans to Route Templates -The route template allows binding beans that are locally scoped and only used as part of creating routes from the template. -This allows using the same template to create multiple routes, where beans are local (private) for each created route. - -For example, given the following route template where we use `templateBean` to set up the local bean as shown: - -._Java-only: templateBean with lambda supplier_ -[source,java] ----- -routeTemplate("s3template") - .templateParameter("region") - .templateParameter("bucket") - .templateBean("myClient", S3Client.class, rtc -> - S3Client.builder().region(rtc.getProperty("region", Region.class)).build(); - ) - .from("direct:s3-store") - // must refer to the bean with {{myClient}} - .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") ----- - -The template has two parameters to specify the AWS region and the S3 bucket. To connect to S3 -then a `software.amazon.awssdk.services.s3.S3Client` bean is necessary. - -To create this bean, we specify this with the `templateBean` DSL where we specify the bean id as `myClient`. -The type of the bean can be specified (`S3Client.class`), however, it is optional, and -can be used if you need to let beans be discovered by type and not by name. - -This ensures that the code creating the bean is executed later (when Camel is creating a route from the template), -then the code must be specified as a _supplier_. Because we want during creation of the bean access to template parameters, -we use a Camel `BeanSupplier` which gives access to `RouteTemplateContext` that is the `_rtc_` variable in the code above. - -IMPORTANT: The local bean with id `myClient` *must* be referred to using Camel's property placeholder syntax, eg `{\{myClient}}` -in the route template, as shown above with the _to_ endpoint. This is because the local -bean must be made unique and Camel will internally re-assign the bean id to use a unique id instead of `myClient`. And this is done with the help -of the property placeholder functionality. - -If multiple routes are created from this template, then each of the created routes have their own -`S3Client` bean created. - -=== Binding beans to route templates from template builder - -The `TemplatedRouteBuilder` also allows to bind local beans (which allows specifying those beans) when -creating routes from existing templates. - -Suppose the route template below is defined in XML: - -._XML-only: route template without bean binding_ -[source,xml] ----- - <routeTemplate id="s3template"> - <templateParameter name="region"/> - <templateParameter name="bucket"/> - <route> - <from uri="direct:s3-store"/> - <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> - </route> - </routeTemplate> ----- - -The template has no bean bindings for `#{\{myClient}}` which would be required for creating the template. - -When creating routes form the template via `TemplatedRouteBuilder` then you can provide the bean binding -if you desire the bean to be locally scoped (not shared with others): - -[tabs] -==== - -Java:: -+ -[source,java] ----- -TemplatedRouteBuilder.builder(context, "s3template") - .parameter("region", "US-EAST-1") - .parameter("bucket", "myBucket") - .bean("myClient", S3Client.class, - S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build()) - .routeId("mys3route") - .add(); ----- -+ -As you can see the binding is similar to when using `templateBean` directly in the route template. - -Java DSL:: -+ -[source,java] ----- -templatedRoute("s3template") - .parameter("region", "US-EAST-1") - .parameter("bucket", "myBucket") - .bean("myClient", S3Client.class, - rtc -> S3Client.builder() // <1> - .region(rtc.getProperty("region", Region.class)) - .build()) - .routeId("mys3route"); ----- -<1> Note that the third parameter of the `bean` method is not directly the bean but rather a factory method that will be used to create the bean, here we use a lambda expression as factory method. - -XML:: -+ -[source,xml] ----- - <templatedRoute routeTemplateRef="s3template" routeId="mys3route"> - <parameter name="region" value="US-EAST-1"/> - <parameter name="bucket" value="myBucket"/> - <bean name="myClient" type="software.amazon.awssdk.services.s3.S3Client" - scriptLanguage="groovy"> <!--1--> - <script> - import software.amazon.awssdk.services.s3.S3Client - S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build() - </script> - </bean> - </templatedRoute> ----- -<1> For non-Java DSL, in case of a complex bean factory, you can still rely on a language like `groovy` to define your bean factory inside a `script` element. - -YAML:: -+ -[source,yaml] ----- -- templatedRoute: - routeTemplateRef: "s3template" - routeId: "mys3route" - parameters: - - name: "region" - value: "US-EAST-1" - - name: "bucket" - value: "myBucket" - beans: - - name: "myClient" - type: "software.amazon.awssdk.services.s3.S3Client" - scriptLanguage: "groovy" - script: | # <1> - import software.amazon.awssdk.services.s3.S3Client - S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build() ----- -<1> For non-Java DSL, in case of a complex bean factory, you can still rely on a language like `groovy` to define your bean factory as value of the `script` key. -==== - -Instead of binding the beans from the template builder, you could also create the bean outside the template, -and bind it by reference. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build(); - -TemplatedRouteBuilder.builder(context, "s3template") - .parameter("region", Region.US_EAST_1) - .parameter("bucket", "myBucket") - .bean("myClient", myClient) - .routeId("mys3route") - .add(); ----- - -Java DSL:: -+ -[source,java] ----- -final S3Client myClient = S3Client.builder().region(Region.US_EAST_1).build(); - -templatedRoute("s3template") - .parameter("region", "US-EAST-1") - .parameter("bucket", "myBucket") - .bean("myClient", S3Client.class, rtc -> myClient) - .routeId("mys3route"); ----- -==== - -NOTE: You should prefer to create the local beans directly from within the template (if possible) because this -ensures the route template has this out of the box. Otherwise, the bean must be created or provided every time -a new route is created from the route template. However, the latter gives freedom to create the bean in any other custom way. - - -=== Binding beans to route templates using bean types - -You can create a local bean by referring to a fully qualified class name which Camel will use to create -a new local bean instance. When using this, the created bean is created via default constructor of the class. - -The bean instance can be configured with properties via getter/setter style. -The previous example with creating the AWS S3Client would not support this kind as this uses _fluent builder_ pattern (not getter/setter). - -TIP: In *Camel 4.6* onwards, you can also use constructor arguments for beans - -So suppose we have a class as follows: - -._Java-only: bean class with getter/setter properties_ -[source,java] ----- -public class MyBar { - private String name; - private String address; - - // getter/setter omitted - - public String location() { - return "The bar " + name + " is located at " + address; - } -} ----- - -Then we can use the `MyBar` class as a local bean in a route template as follows: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -routeTemplate("barTemplate") - .templateParameter("bar") - .templateParameter("street") - .templateBean("myBar") - .typeClass("com.foo.MyBar") - .property("name", "{{bar}}") - .property("address", "{{street}}") - .end() - .from("direct:going-out") - .to("bean:{{myBar}}"); ----- -+ -With Java DSL, you can also refer to the bean class using type safe way in `typeClass` by referring to the `.class` as follows: -+ -[source,java] ----- -routeTemplate("barTemplate") - .templateParameter("bar") - .templateParameter("street") - .templateBean("myBar") - .typeClass(MyBar.class) - .property("name", "{{bar}}") - .property("address", "{{street}}") - .end() - .from("direct:going-out") - .to("bean:{{myBar}}"); ----- - -XML:: -+ -In XML, we specify the class FQN name using `#class:` syntax as shown: -+ -[source,xml] ----- -<routeTemplate id="myBar"> - <templateParameter name="bar"/> - <templateParameter name="street"/> - <templateBean name="myBean" type="#class:com.foo.MyBar"> - <properties> - <property key="name" value="{{bar}}"/> - <property key="address" value="{{street}}"/> - </properties> - </templateBean> - <route> - <from uri="direct:going-out"/> - <to uri="bean:{{myBar}}"/> - </route> -</routeTemplate> ----- - -YAML:: -+ -In YAML, we specify the class FQN name using `#class:` syntax as shown: -+ -[source,yaml] ----- -- routeTemplate: - id: "myBar" - parameters: - - name: "bar" - - name: "street" - beans: - - name: "myBean" - type: "#class:com.foo.MyBar" - properties: - name: "{{bar}}" - address: "{{street}}" - from: - uri: direct:going-out - steps: - - to: - uri: "bean:{{myBar}}" ----- -==== - - -=== Binding beans to route templates using scripting languages (advanced) - -You can use scripting languages like Groovy, Java, to create the bean. -This allows defining route templates with the scripting language supported by Camel such as Groovy. - -For example, creating the AWS S3 client can be done as shown in Java (with inlined Groovy code): - -[tabs] -==== - -Java:: -+ -[source,java] ----- -routeTemplate("s3template") - .templateParameter("region") - .templateParameter("bucket") - .templateBean("myClient", "groovy", - "software.amazon.awssdk.services.s3.S3Client.S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build()" - ) - .from("direct:s3-store") - // must refer to the bean with {{myClient}} - .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") ----- - -XML:: -+ -Notice how the Groovy code can be inlined directly in the route template in XML also. -+ -[source,xml] ----- - <routeTemplate id="s3template"> - <templateParameter name="region"/> - <templateParameter name="bucket"/> - <templateBean name="myClient" scriptLanguage="groovy"> - <script> - import software.amazon.awssdk.services.s3.S3Client - S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build() - </script> - </templateBean> - <route> - <from uri="direct:s3-store"/> - <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> - </route> - </routeTemplate> ----- - -YAML:: -+ -Notice how the Groovy code can be inlined directly in the route template in DSL also. -+ -[source,yaml] ----- -- routeTemplate: - id: "s3template" - parameters: - - name: "region" - - name: "bucket" - beans: - - name: "myClient" - scriptLanguage: "groovy" - script: | - import software.amazon.awssdk.services.s3.S3Client - S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build() - from: - uri: direct:s3-store - steps: - - to: - uri: "aws2-s3:{{bucket}}" - parameters: - amazonS3Client: "#{{myClient}}" ----- -==== - -The groovy code can be externalized into a file on the classpath or file system, by using `resource:` as prefix, such as: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -routeTemplate("s3template") - .templateParameter("region") - .templateParameter("bucket") - .templateBean("myClient", "groovy", "resource:classpath:s3bean.groovy") - .from("direct:s3-store") - // must refer to the bean with {{myClient}} - .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") ----- - -XML:: -+ -Notice how we can tell Camel the type of the bean is `software.amazon.awssdk.services.s3.S3Client` which allows -Camel to better understand, and also makes the bean able for autowiring and binding via type. The `type` is optional -and can be omitted. For example in this example as we inject the bean via its bean id `myClient`. -+ -[source,xml] ----- - <routeTemplate id="s3template"> - <templateParameter name="region"/> - <templateParameter name="bucket"/> - <templateBean name="myClient" scriptLanguage="groovy" - type="software.amazon.awssdk.services.s3.S3Client"> - <script>resource:classpath:s3bean.groovy</script> - </templateBean> - <route> - <from uri="direct:s3-store"/> - <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> - </route> - </routeTemplate> ----- - -YAML:: -+ -Notice how we can tell Camel the type of the bean is `software.amazon.awssdk.services.s3.S3Client` which allows -Camel to better understand, and also makes the bean able for autowiring and binding via type. The `type` is optional -and can be omitted. For example in this example as we inject the bean via its bean id `myClient`. -+ -[source,yaml] ----- -- routeTemplate: - id: "s3template" - parameters: - - name: "region" - - name: "bucket" - beans: - - name: "myClient" - scriptLanguage: "groovy" - type: "software.amazon.awssdk.services.s3.S3Client" - script: "resource:classpath:s3bean.groovy" - from: - uri: direct:s3-store - steps: - - to: - uri: "aws2-s3:{{bucket}}" - parameters: - amazonS3Client: "#{{myClient}}" ----- -==== - -Then create the file `s3bean.groovy` in the classpath root: - -.s3bean.groovy -[source,groovy] ----- -import software.amazon.awssdk.services.s3.S3Client - -S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build() ----- - -The languages supported you can specify as `scriptLanguage` are: - -[width="100%",cols="2s,8",options="header"] -|=== -| Type | Description -| bean | Calling a method on a Java class to create the bean. -| groovy | Using a groovy script to create the bean. -| java | Java code which is runtime compiled (using jOOR library) to create the bean. -| mvel | To use a MVEL template script to create the bean. -| ognl | To use OGNL template script to create the bean. -| _name_ | To use a third-party language by the given `_name_` to create the bean. -|=== - -Camel will bind `RouteTemplateContext` as the root object with name `rtc` when evaluating the script. -This means you can get access to all the information from `RouteTemplateContext` and `CamelContext` via `rtc`. - -This is what we have done in the scripts in the previous examples where we get hold of a template parameter with: - -[source,groovy] ----- -rtc.getProperty('region', String.class) ----- - -To get access to `CamelContext` you can do: - -[source,groovy] ----- -var cn = rtc.getCamelContext().getName() ----- - -The most powerful languages to use are groovy and java. The other languages are limited in flexibility -as they are not complete programming languages, but are more suited for templating needs. - -It is recommended to either use groovy or java, if creating the local bean requires coding, -and the route templates are not defined using Java code. - -The bean language can be used when creating the local bean from an existing Java method (static or not-static method), -and the route templates are not defined using Java code. - -For example suppose there is a class named `com.foo.MyAwsHelper` that has a method called `createS3Client` -then you can call this method from the route template as shown in the following: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -routeTemplate("s3template") - .templateParameter("region") - .templateParameter("bucket") - .templateBean("myClient", "beam", "com.foo.MyAwsHelper?method=createS3Client") - .from("direct:s3-store") - // must refer to the bean with {{myClient}} - .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") ----- - -XML:: -+ -[source,xml] ----- - <routeTemplate id="s3template"> - <templateParameter name="region"/> - <templateParameter name="bucket"/> - <templateBean name="myClient" type="bean"> - <script>com.foo.MyAwsHelper?method=createS3Client</script> - </templateBean> - <route> - <from uri="direct:s3-store"/> - <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> - </route> - </routeTemplate> ----- - -YAML:: -+ -[source,yaml] ----- -- routeTemplate: - id: "s3template" - parameters: - - name: "region" - - name: "bucket" - beans: - - name: "myClient" - scriptLanguage: "bean" - script: "com.foo.MyAwsHelper?method=createS3Client" - from: - uri: direct:s3-store - steps: - - to: - uri: "aws2-s3:{{bucket}}" - parameters: - amazonS3Client: "#{{myClient}}" ----- -==== - -The method signature of `createS3Client` method **MUST** then have one parameter for the `RouteTemplateContext` as shown: - -._Java-only: bean factory method signature_ -[source,java] ----- -public static S3Client createS3Client(RouteTemplateContext rtc) { - return S3Client.builder() - .region(rtc.getProperty("region", Region.class)) - .build(); -} ----- - -If you are using pure Java code (both template and creating local bean), -then you can create the local bean using Java lambda style as previously documented. - - -==== Configuring the type of the created bean - -The `type` must be set to define what FQN class the created bean. - -In the following route template we want to tell Camel that the bean is a `software.amazon.awssdk.services.s3.S3Client` type. - - -[tabs] -==== - -Java:: -+ -In Java DSL you can refer to the type via its class (ie `S3Client.java` which is a type safe way: -+ -[source,java] ----- -routeTemplate("s3template") - .templateParameter("region") - .templateParameter("bucket") - .templateBean("myClient", S3Client.class, "bean", "com.foo.MyAwsHelper?method=createS3Client") - .from("direct:s3-store") - // must refer to the bean with {{myClient}} - .to("aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}") ----- - -XML:: -+ -[source,xml] ----- - <routeTemplate id="s3template"> - <templateParameter name="region"/> - <templateParameter name="bucket"/> - <templateBean name="myClient" scriptLanguage="bean" - type="software.amazon.awssdk.services.s3.S3Client"> - <script>com.foo.MyAwsHelper?method=createS3Client</script> - </templateBean> - <route> - <from uri="direct:s3-store"/> - <to uri="aws2-s3:{{bucket}}?amazonS3Client=#{{myClient}}"/> - </route> - </routeTemplate> ----- - -YAML:: -+ -[source,yaml] ----- -- routeTemplate: - id: "s3template" - parameters: - - name: "region" - - name: "bucket" - beans: - - name: "myClient" - scriptLanguage: "bean" - type: "software.amazon.awssdk.services.s3.S3Client" - script: "com.foo.MyAwsHelper?method=createS3Client" - from: - uri: direct:s3-store - steps: - - to: - uri: "aws2-s3:{{bucket}}" - parameters: - amazonS3Client: "#{{myClient}}" ----- -==== +Route templates support binding locally-scoped beans that are private to each route created from the template. +This is an advanced feature. +See xref:route-template-bean-binding.adoc[Route Template Bean Binding] for details. == Configuring route templates when creating route (advanced) diff --git a/docs/user-manual/modules/ROOT/pages/security.adoc b/docs/user-manual/modules/ROOT/pages/security.adoc index da6512831e5c..9ea97369884c 100644 --- a/docs/user-manual/modules/ROOT/pages/security.adoc +++ b/docs/user-manual/modules/ROOT/pages/security.adoc @@ -100,62 +100,50 @@ The following _Vaults_ are supported by Camel: * xref:components::hashicorp-vault-component.adoc[Hashicorp Vault] * xref:components::ibm-secrets-manager-component.adoc[IBM Secrets Manager] -==== Using AWS Vault - -To use AWS Secrets Manager, you need to provide _accessKey_, _secretKey_ and the _region_. -This can be done using environmental variables before starting the application: - -[source,bash] ----- -export $CAMEL_VAULT_AWS_ACCESS_KEY=accessKey -export $CAMEL_VAULT_AWS_SECRET_KEY=secretKey -export $CAMEL_VAULT_AWS_REGION=region ----- - -You can also configure the credentials in the `application.properties` file such as: - -[source,properties] ----- -camel.vault.aws.accessKey = accessKey -camel.vault.aws.secretKey = secretKey -camel.vault.aws.region = region ----- - -If you want instead to use the https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html[AWS default credentials provider], you'll need to provide the following env variables: - -[source,bash] ----- -export $CAMEL_VAULT_AWS_USE_DEFAULT_CREDENTIALS_PROVIDER=true -export $CAMEL_VAULT_AWS_REGION=region ----- +==== Vault Usage Syntax -You can also configure the credentials in the `application.properties` file such as: +All vault providers use the same property placeholder syntax. The prefix identifies which vault to use: -[source,properties] ----- -camel.vault.aws.defaultCredentialsProvider = true -camel.vault.aws.region = region ----- +[width="100%",cols="2,1,3",options="header"] +|=== +| Provider | Prefix | Required JAR +| AWS Secrets Manager | `aws:` | `camel-aws-secrets-manager` +| Google Secret Manager | `gcp:` | `camel-google-secret-manager` +| Azure Key Vault | `azure:` | `camel-azure-key-vault` +| Hashicorp Vault | `hashicorp:` | `camel-hashicorp-vault` +| IBM Secrets Manager | `ibm:` | `camel-ibm-secrets-manager` +| CyberArk Conjur | `cyberark:` | `camel-cyberark-vault` +|=== -It is also possible to specify a particular profile name for accessing AWS Secrets Manager +The following syntax forms are supported (replace `<prefix>` with the provider prefix from the table above): -[source,bash] +[source,text] ---- -export $CAMEL_VAULT_AWS_USE_PROFILE_CREDENTIALS_PROVIDER=true -export $CAMEL_VAULT_AWS_PROFILE_NAME=test-account -export $CAMEL_VAULT_AWS_REGION=region +{{<prefix>:secretName}} <1> +{{<prefix>:secretName:defaultValue}} <2> +{{<prefix>:secretName#fieldName}} <3> +{{<prefix>:secretName#fieldName:defaultValue}} <4> ---- +<1> Lookup the secret value. Fails if the secret does not exist. +<2> Lookup the secret value, falling back to `defaultValue` if the secret does not exist. +<3> Lookup a specific field from a JSON-structured secret. +<4> Lookup a specific field, with a fallback default value. -You can also configure the credentials in the `application.properties` file such as: +For example, if you have a JSON secret named `database`: -[source,properties] +[source,json] ---- -camel.vault.aws.profileCredentialsProvider = true -camel.vault.aws.profileName = test-account -camel.vault.aws.region = region +{ + "username": "admin", + "password": "password123", + "engine": "postgres", + "host": "127.0.0.1", + "port": "3128", + "dbname": "db" +} ---- -At this point you'll be able to reference a property in the following way by using `aws:` as prefix in the `{{ }}` syntax: +You can access individual fields using the `#` syntax: [tabs] ==== @@ -165,7 +153,8 @@ Java:: [source,java] ---- from("direct:start") - .to("{{aws:route}}"); + .log("Username is {{aws:database#username}}") + .log("Password is {{aws:database#password:secret}}"); ---- XML:: @@ -174,7 +163,8 @@ XML:: ---- <route> <from uri="direct:start"/> - <to uri="{{aws:route}}"/> + <log message="Username is {{aws:database#username}}"/> + <log message="Password is {{aws:database#password:secret}}"/> </route> ---- @@ -186,143 +176,90 @@ YAML:: from: uri: direct:start steps: - - to: - uri: "{{aws:route}}" + - log: + message: "Username is {{aws:database#username}}" + - log: + message: "Password is {{aws:database#password:secret}}" ---- ==== -Where `route` will be the name of the secret stored in the AWS Secrets Manager Service. - -You could specify a default value (`aws:key:default-value`) in case the secret is not present on AWS Secret Manager: +The examples above use the `aws:` prefix but the same syntax works with any provider prefix (`gcp:`, `azure:`, `hashicorp:`, `ibm:`, `cyberark:`). -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{aws:route:myDefault}}"); ----- +===== Version Syntax -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{aws:route:myDefault}}"/> -</route> ----- +Hashicorp Vault, IBM Secrets Manager, and CyberArk Conjur support retrieving a specific version of a secret using the `@` suffix: -YAML:: -+ -[source,yaml] +[source,text] ---- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{aws:route:myDefault}}" +{{hashicorp:secret:route@2}} <1> +{{hashicorp:route:defaultValue@2}} <2> +{{hashicorp:secret:database#username:admin@2}} <3> +{{ibm:default:route@2}} <4> +{{cyberark:route@2}} <5> ---- -==== +<1> Hashicorp: get secret `route` at version 2 from the `secret` engine. +<2> Hashicorp: get secret `route` at version 2, with default fallback. +<3> Hashicorp: get field `username` from secret `database` at version 2. +<4> IBM: get secret `route` at version 2 from the `default` secret group. +<5> CyberArk: get secret `route` at version 2. -In this case, if the secret doesn't exist, the property will fallback to "myDefault" as value. +==== Using AWS Vault -Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form: +To use AWS Secrets Manager, you need to provide _accessKey_, _secretKey_ and the _region_. +This can be done using environmental variables before starting the application: -[source,json] +[source,bash] ---- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} +export $CAMEL_VAULT_AWS_ACCESS_KEY=accessKey +export $CAMEL_VAULT_AWS_SECRET_KEY=secretKey +export $CAMEL_VAULT_AWS_REGION=region ---- -You're able to do get single secret value in your route, like for example: - -[tabs] -==== +You can also configure the credentials in the `application.properties` file such as: -Java:: -+ -[source,java] +[source,properties] ---- -from("direct:start") - .log("Username is {{aws:database#username}}"); +camel.vault.aws.accessKey = accessKey +camel.vault.aws.secretKey = secretKey +camel.vault.aws.region = region ---- -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{aws:database#username}}"/> -</route> ----- +If you want instead to use the https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials.html[AWS default credentials provider], you'll need to provide the following env variables: -YAML:: -+ -[source,yaml] +[source,bash] ---- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{aws2:database#username}}" +export $CAMEL_VAULT_AWS_USE_DEFAULT_CREDENTIALS_PROVIDER=true +export $CAMEL_VAULT_AWS_REGION=region ---- -==== - -Or re-use the property as part of an endpoint. -You could specify a default value in case the particular field of secret is not present on AWS Secret Manager: - -[tabs] -==== +You can also configure the credentials in the `application.properties` file such as: -Java:: -+ -[source,java] +[source,properties] ---- -from("direct:start") - .log("Username is {{aws:database#username:admin}}"); +camel.vault.aws.defaultCredentialsProvider = true +camel.vault.aws.region = region ---- +It is also possible to specify a particular profile name for accessing AWS Secrets Manager: -XML:: -+ -[source,xml] +[source,bash] ---- -<route> - <from uri="direct:start"/> - <log message="Username is {{aws:database#username:admin}}"/> -</route> +export $CAMEL_VAULT_AWS_USE_PROFILE_CREDENTIALS_PROVIDER=true +export $CAMEL_VAULT_AWS_PROFILE_NAME=test-account +export $CAMEL_VAULT_AWS_REGION=region ---- -YAML:: -+ -[source,yaml] +You can also configure the credentials in the `application.properties` file such as: + +[source,properties] ---- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{aws:database#username:admin}}" +camel.vault.aws.profileCredentialsProvider = true +camel.vault.aws.profileName = test-account +camel.vault.aws.region = region ---- -==== -In this case, if the secret doesn't exist or the secret exists, but the username field is not part of the secret, the property will fall back to "admin" as value. - -NOTE: For the moment we are not considering the rotation function if any are applied, but it is in the work to be done. +At this point you'll be able to reference a property by using `aws:` as prefix in the `{{ }}` syntax. +See <<Vault Usage Syntax>> for the full syntax reference. The only requirement is adding `camel-aws-secrets-manager` JAR to your Camel application. @@ -358,1019 +295,131 @@ You can also configure the credentials in the `application.properties` file such [source,properties] ---- camel.vault.gcp.useDefaultInstance = true -camel.vault.aws.projectId = region +camel.vault.gcp.projectId = projectId ---- -At this point you'll be able to reference a property in the following way by using `gcp:` as prefix in the `{{ }}` syntax: +At this point you'll be able to reference a property by using `gcp:` as prefix in the `{{ }}` syntax. +See <<Vault Usage Syntax>> for the full syntax reference. -[tabs] -==== +There are only two requirements: +- Adding `camel-google-secret-manager` JAR to your Camel application. +- Give the service account used permissions to do operation at secret management level (for example accessing the secret payload, or being admin of secret manager service) -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{gcp:route}}"); ----- +==== Using Azure Key Vault -XML:: -+ -[source,xml] ----- - <route> - <from uri="direct:start"/> - <to uri="{{gcp:route}}"/> - </route> ----- +To use this function, you'll need to provide credentials to Azure Key Vault Service as environment variables: -YAML:: -+ -[source,yaml] +[source,bash] ---- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{gcp:route}}" +export $CAMEL_VAULT_AZURE_TENANT_ID=tenantId +export $CAMEL_VAULT_AZURE_CLIENT_ID=clientId +export $CAMEL_VAULT_AZURE_CLIENT_SECRET=clientSecret +export $CAMEL_VAULT_AZURE_VAULT_NAME=vaultName ---- -==== - -Where `route` will be the name of the secret stored in the GCP Secret Manager Service. -You could specify a default value (`gcp:key:default-value`) in case the secret is not present GCP Secret Manager: - -[tabs] -==== +You can also configure the credentials in the `application.properties` file such as: -Java:: -+ -[source,java] +[source,properties] ---- -from("direct:start") - .to("{{gcp:route:myDefault}}"); +camel.vault.azure.tenantId = accessKey +camel.vault.azure.clientId = clientId +camel.vault.azure.clientSecret = clientSecret +camel.vault.azure.vaultName = vaultName ---- -XML:: -+ -[source,xml] +Or you can enable the usage of Azure Identity in the following way: + +[source,bash] ---- -<route> - <from uri="direct:start"/> - <to uri="{{gcp:route:myDefault}}"/> -</route> +export $CAMEL_VAULT_AZURE_IDENTITY_ENABLED=true +export $CAMEL_VAULT_AZURE_VAULT_NAME=vaultName ---- -YAML:: -+ -[source,yaml] +You can also enable the usage of Azure Identity in the `application.properties` file such as: + +[source,properties] ---- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{gcp:route:myDefault}}" +camel.vault.azure.azureIdentityEnabled = true +camel.vault.azure.vaultName = vaultName ---- -==== -In this case, if the secret doesn't exist, the property will fallback to "myDefault" as value. +At this point you'll be able to reference a property by using `azure:` as prefix in the `{{ }}` syntax. +See <<Vault Usage Syntax>> for the full syntax reference. -Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form: +The only requirement is adding the `camel-azure-key-vault` JAR to your Camel application. -[source,json] +==== Using Hashicorp Vault + +To use this function, you'll need to provide credentials for Hashicorp vault as environment variables: + +[source,bash] ---- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} ----- - -You're able to do get single secret value in your route, like for example: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{gcp:database#username}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{gcp:database#username}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{gcp:database#username}}" ----- -==== - -Or re-use the property as part of an endpoint. - -You could specify a default value in case the particular field of secret is not present on GCP Secret Manager: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{gcp:database#username:admin}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{gcp:database#username:admin}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{gcp:database#username:admin}}" ----- -==== - -In this case, if the secret doesn't exist or the secret exists, but the username field is not part of the secret, the property will fallback to "admin" as value. - -NOTE: For the moment we are not considering the rotation function if any are applied, but it is in the work to be done. - -There are only two requirements: -- Adding `camel-google-secret-manager` JAR to your Camel application. -- Give the service account used permissions to do operation at secret management level (for example accessing the secret payload, or being admin of secret manager service) - -==== Using Azure Key Vault - -To use this function, you'll need to provide credentials to Azure Key Vault Service as environment variables: - -[source,bash] ----- -export $CAMEL_VAULT_AZURE_TENANT_ID=tenantId -export $CAMEL_VAULT_AZURE_CLIENT_ID=clientId -export $CAMEL_VAULT_AZURE_CLIENT_SECRET=clientSecret -export $CAMEL_VAULT_AZURE_VAULT_NAME=vaultName +export $CAMEL_VAULT_HASHICORP_TOKEN=token +export $CAMEL_VAULT_HASHICORP_HOST=host +export $CAMEL_VAULT_HASHICORP_PORT=port +export $CAMEL_VAULT_HASHICORP_SCHEME=http/https ---- You can also configure the credentials in the `application.properties` file such as: [source,properties] ---- -camel.vault.azure.tenantId = accessKey -camel.vault.azure.clientId = clientId -camel.vault.azure.clientSecret = clientSecret -camel.vault.azure.vaultName = vaultName +camel.vault.hashicorp.token = token +camel.vault.hashicorp.host = host +camel.vault.hashicorp.port = port +camel.vault.hashicorp.scheme = scheme ---- -Or you can enable the usage of Azure Identity in the following way: +If the running Hashicorp Vault instance is on Hashicorp Cloud, two additional parameters are required: [source,bash] ---- -export $CAMEL_VAULT_AZURE_IDENTITY_ENABLED=true -export $CAMEL_VAULT_AZURE_VAULT_NAME=vaultName +export CAMEL_HASHICORP_VAULT_CLOUD=true +export CAMEL_HASHICORP_VAULT_NAMESPACE=namespace ---- -You can also enable the usage of Azure Identity in the `application.properties` file such as: +Or in `application.properties`: [source,properties] ---- -camel.vault.azure.azureIdentityEnabled = true -camel.vault.azure.vaultName = vaultName ----- - -At this point, you'll be able to reference a property in the following way: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{azure:route}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{azure:route}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{azure:route}}" ----- -==== - -Where route will be the name of the secret stored in the Azure Key Vault Service. - -You could specify a default value (`azure:key:default-value`) in case the secret is not present on Azure Key Value Service: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{azure:route:myDefault}}"); +camel.vault.hashicorp.cloud = true +camel.vault.hashicorp.namespace = namespace ---- -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{azure:route:myDefault}}"/> -</route> ----- +At this point you'll be able to reference a property by using `hashicorp:` as prefix in the `{{ }}` syntax. +Hashicorp Vault also supports <<Version Syntax,version-specific lookups>> using the `@` suffix. +See <<Vault Usage Syntax>> for the full syntax reference. -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{azure:route:myDefault}}" ----- -==== +The only requirement is adding the `camel-hashicorp-vault` JAR to your Camel application. -In this case, if the secret doesn't exist, the property will fallback to "myDefault" as value. +==== Using IBM Secrets Manager Vault -Also you are able to get a particular field of the secret if you have, for example, a secret named database of this form: +To use this function, you'll need to provide credentials for IBM Secrets Manager vault as environment variables: [source,bash] ---- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} ----- - -You're able to do get single secret value in your route, like for example: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{azure:database#username}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{azure:database#username}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{azure:database#username}}" ----- -==== - -Or re-use the property as part of an endpoint. - -You could specify a default value in case the particular field of secret is not present on Azure Key Vault: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{azure:database#username:admin}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{azure:database#username:admin}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{azure:database#username:admin}}" +export CAMEL_VAULT_IBM_TOKEN=token +export CAMEL_VAULT_IBM_SERVICE_URL=serviceUrl ---- -==== - -In this case, if the secret doesn't exist or the secret exists, but the username field is not part of the secret, the property will fallback to "admin" as value. - -For the moment we are not considering the rotation function if any are applied, but it is in the work to be done. - -The only requirement is adding the camel-azure-key-vault jar to your Camel application. - -==== Using Hashicorp Vault -To use this function, you'll need to provide credentials for Hashicorp vault as environment variables: - -[source,bash] ----- -export $CAMEL_VAULT_HASHICORP_TOKEN=token -export $CAMEL_VAULT_HASHICORP_HOST=host -export $CAMEL_VAULT_HASHICORP_PORT=port -export $CAMEL_VAULT_HASHICORP_SCHEME=http/https ----- - -You can also configure the credentials in the `application.properties` file such as: - -[source,properties] ----- -camel.vault.hashicorp.token = token -camel.vault.hashicorp.host = host -camel.vault.hashicorp.port = port -camel.vault.hashicorp.scheme = scheme ----- - -In case the running Hashicorp Vault instance you're pointing is running on Hashicorp Cloud, the configuration will require two additional parameters: - -[source,bash] ----- -export CAMEL_VAULT_HASHICORP_TOKEN=token -export CAMEL_VAULT_HASHICORP_HOST=host -export CAMEL_VAULT_HASHICORP_PORT=port -export CAMEL_VAULT_HASHICORP_SCHEME=http/https -export CAMEL_HASHICORP_VAULT_CLOUD=true -export CAMEL_HASHICORP_VAULT_NAMESPACE=namespace ----- - -You can also set the same in the `application.properties` file such as: - -[source,properties] ----- -camel.vault.hashicorp.token = token -camel.vault.hashicorp.host = host -camel.vault.hashicorp.port = port -camel.vault.hashicorp.scheme = scheme -camel.vault.hashicorp.cloud = true -camel.vault.hashicorp.namespace = namespace ----- - -At this point, you'll be able to reference a property in the following way: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{hashicorp:route}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{hashicorp:route}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{hashicorp:route}}" ----- -==== - -Where route will be the name of the secret stored in the Hashicorp Vault instance, in the 'secret' engine. - -You could specify a default value in case the secret is not present on Hashicorp Vault instance: - -You could specify a default value (`azure:key:default-value`) in case the secret is not present on Azure Key Value Service: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{hashicorp:route:myDefault}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{hashicorp:route:myDefault}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{hashicorp:route:myDefault}}" ----- -==== - -In this case, if the secret doesn't exist, the property will fallback to "myDefault" as value. - -Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form: - -[source,bash] ----- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} ----- - -You're able to do get single secret value in your route, in the 'secret' engine, like, for example: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{hashicorp:database#username}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{hashicorp:database#username}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{hashicorp:database#username}}" ----- -==== - -Or re-use the property as part of an endpoint. - -You could specify a default value in case the particular field of secret is not present on Hashicorp Vault instance, in the 'secret' engine: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{hashicorp:database#username:admin}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{hashicorp:database#username:admin}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{hashicorp:database#username:admin}}" ----- -==== - -In this case, if the secret doesn't exist or the secret exists (in the 'secret' engine) but the username field is not part of the secret, the property will fall back to "admin" as value. - -There is also the syntax to get a particular version of the secret for both the approach, with field/default value specified or only with secret: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{hashicorp:secret:route@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{hashicorp:secret:route@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{hashicorp:secret:route@2}}" ----- -==== - -This approach will return the RAW route secret with version '2', in the 'secret' engine. - - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{hashicorp:route:default@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{hashicorp:route:default@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{hashicorp:route:default@2}}" ----- -==== - - -This approach will return the route secret value with version '2' or default value in case the secret doesn't exist or the version doesn't exist (in the 'secret' engine). - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{hashicorp:secret:database#username:admin@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{hashicorp:secret:database#username:admin@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{hashicorp:secret:database#username:admin@2}}" ----- -==== - -This approach will return the username field of the database secret with version '2' or admin in case the secret doesn't exist or the version doesn't exist (in the 'secret' engine). - -==== Using IBM Secrets Manager Vault - -To use this function, you'll need to provide credentials for IBM Secrets Manager vault as environment variables: - -[source,bash] ----- -export CAMEL_VAULT_IBM_TOKEN=token -export CAMEL_VAULT_IBM_SERVICE_URL=serviceUrl ----- - -You can also configure the credentials in the `application.properties` file such as: - -[source,properties] ----- -camel.vault.ibm.token = token -camel.vault.ibm.serviceUrl = serviceUrl ----- - -NOTE: if you're running the application on a Kubernetes based cloud platform, you can initialize the environment variables from a Secret or Configmap to enhance security. You can also enhance security by xref:manual::using-propertyplaceholder.adoc#_resolving_property_placeholders_on_cloud[setting a Secret property placeholder] which will be initialized at application runtime only. - -NOTE: `camel.vault.ibm` configuration only applies to the IBM Secrets Manager Vault properties function (E.g when resolving properties). -When using the `operation` option to create, get, list secrets etc., you should provide the `token` and `serviceUrl` options. - -At this point, you'll be able to reference a property in the following way: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{ibm:route}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{ibm:route}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{ibm:route}}" ----- -==== - -Where route will be the name of the secret stored in the IBM Secrets Manager Vault instance, in the 'default' secret group. - -You could specify a default value in case the secret is not present on IBM Secrets Manager Vault instance: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{ibm:default:route:default}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{ibm:default:route:default}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{ibm:default:route:default}}" ----- -==== - -In this case, if the secret doesn't exist in the 'default' secret group, the property will fall back to "default" as value. - -Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form: - -[source,bash] ----- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} ----- - -You're able to do get single secret value in your route, in the 'default' secret group, like for example: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{ibm:default:database#username}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{ibm:default:database#username}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{ibm:default:database#username}}" ----- -==== - -Or re-use the property as part of an endpoint. - -You could specify a default value in case the particular field of secret is not present on IBM Secrets Manager Vault instance, in the 'secret' engine: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{ibm:default:database#username:admin}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{ibm:default:database#username:admin}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{ibm:default:database#username:admin}}" ----- -==== - -In this case, if the secret doesn't exist or the secret exists (in the 'default' secret group) but the username field is not part of the secret, the property will fall back to "admin" as value. - -There is also the syntax to get a particular version of the secret for both the approaches, with field/default value specified or only with secret: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{ibm:default:route@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{ibm:default:route@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{ibm:default:route@2}}" ----- -==== - -This approach will return the RAW route secret with version '2', in the 'default' secret group. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{ibm:default:route:default@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{ibm:default:route:default@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{ibm:default:route:default@2}}" ----- -==== - -This approach will return the route secret value with version '2' or default value in case the secret doesn't exist or the version doesn't exist (in the 'default' secret group). - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{ibm:default:database#username:admin@2}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{ibm:default:database#username:admin@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] +You can also configure the credentials in the `application.properties` file such as: + +[source,properties] ---- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{ibm:default:database#username:admin@2}}" +camel.vault.ibm.token = token +camel.vault.ibm.serviceUrl = serviceUrl ---- -==== -This approach will return the username field of the database secret with version '2' or admin in case the secret doesn't exist or the version doesn't exist (in the 'default' secret group). +NOTE: If you're running the application on a Kubernetes based cloud platform, you can initialize the environment variables from a Secret or Configmap to enhance security. You can also enhance security by xref:manual::using-propertyplaceholder.adoc#_resolving_property_placeholders_on_cloud[setting a Secret property placeholder] which will be initialized at application runtime only. + +NOTE: `camel.vault.ibm` configuration only applies to the IBM Secrets Manager Vault properties function (E.g when resolving properties). +When using the `operation` option to create, get, list secrets etc., you should provide the `token` and `serviceUrl` options. + +IBM Secrets Manager organizes secrets into _secret groups_. The syntax includes the group name (e.g. `default`): +`{{ibm:default:secretName}}`, `{{ibm:default:secretName:fallback}}`, `{{ibm:default:secretName#field}}`, +`{{ibm:default:secretName#field:fallback}}`. +IBM also supports <<Version Syntax,version-specific lookups>> using the `@` suffix. +See <<Vault Usage Syntax>> for the full syntax reference. The only requirement is adding the `camel-ibm-secrets-manager` JAR to your Camel application. @@ -1419,286 +468,12 @@ camel.vault.cyberark.password = secretpassword NOTE: If you're running the application on a Kubernetes based cloud platform, you can initialize the environment variables from a Secret or ConfigMap to enhance security. -At this point, you'll be able to reference a property in the following way: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cyberark:route}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cyberark:route}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cyberark:route}}" ----- -==== - -Where `route` will be the name of the secret (variable) stored in the CyberArk Conjur Vault instance. - -You could specify a default value in case the secret is not present on CyberArk Conjur: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cyberark:route:default}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cyberark:route:default}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cyberark:route:default}}" ----- -==== - -In this case, if the secret doesn't exist, the property will fall back to "default" as value. - -Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form: - -[source,json] ----- -{ - "username": "admin", - "password": "password123", - "engine": "postgres", - "host": "127.0.0.1", - "port": "3128", - "dbname": "db" -} ----- - -You're able to get single secret value in your route, like for example: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{cyberark:database#username}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{cyberark:database#username}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{cyberark:database#username}}" ----- -==== - -Or re-use the property as part of an endpoint. - -You could specify a default value in case the particular field of secret is not present on CyberArk Conjur: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{cyberark:database#username:admin}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{cyberark:database#username:admin}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{cyberark:database#username:admin}}" ----- -==== - -In this case, if the secret doesn't exist or the secret exists but the username field is not part of the secret, the property will fall back to "admin" as value. - -There is also the syntax to get a particular version of the secret for both the approaches, with field/default value specified or only with secret: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cyberark:route@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cyberark:route@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cyberark:route@2}}" ----- -==== - -This approach will return the RAW route secret with version '2'. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{cyberark:route:default@2}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{cyberark:route:default@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{cyberark:route:default@2}}" ----- -==== - -This approach will return the route secret value with version '2' or default value in case the secret doesn't exist or the version doesn't exist. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("Username is {{cyberark:database#username:admin@2}}"); ----- - - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <log message="Username is {{cyberark:database#username:admin@2}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "Username is {{cyberark:database#username:admin@2}}" ----- -==== - -This approach will return the username field of the database secret with version '2' or admin in case the secret doesn't exist or the version doesn't exist. - NOTE: CyberArk Conjur requires secrets (variables) to be defined in a policy file before they can be set. +At this point you'll be able to reference a property by using `cyberark:` as prefix in the `{{ }}` syntax. +CyberArk also supports <<Version Syntax,version-specific lookups>> using the `@` suffix. +See <<Vault Usage Syntax>> for the full syntax reference. + The only requirement is adding the `camel-cyberark-vault` JAR to your Camel application. diff --git a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc index 28e6aa917433..d48d76caa862 100644 --- a/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc +++ b/docs/user-manual/modules/ROOT/pages/using-propertyplaceholder.adoc @@ -712,745 +712,12 @@ the `${}` notation. And in the Camel routes we use the Camel placeholder notation with `{\{key}}`. -== Using property placeholder functions +== Property Placeholder Functions -The xref:components::properties-component.adoc[Properties] component includes the following functions out of the box: - -* `env` - A function to lookup the property from OS environment variables -* `sys` - A function to lookup the property from Java JVM system properties -* `bean` - A function to lookup the property from the return value of bean's method (requires `camel-bean` JAR) -* `boolean` - A function to evaluate if a property key matches a condition and returns either `true` or `false` -* `service` - A function to lookup the property from OS environment variables using the service naming idiom -* `service.name` - A function to lookup the property from OS environment variables using the service naming idiom returning the hostname part only -* `service.port` - A function to lookup the property from OS environment variables using the service naming idiom returning the port part only - -These functions are intended to make it easy to lookup values from the environment, as shown in the example below: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{env:SOMENAME}}") - .to("{{sys:MyJvmPropertyName}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{env:SOMENAME}}"/> - <to uri="{{sys:MyJvmPropertyName}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{env:SOMENAME}}" - - to: - uri: "{{sys:MyJvmPropertyName}}" ----- -==== - -You can use default values as well, so if the property does not exist, you can define a default value as shown below, where the default value is a `log:foo` and `log:bar` value. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{env:SOMENAME:log:foo}}") - .to("{{sys:MyJvmPropertyName:log:bar}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{env:SOMENAME:log:foo}}"/> - <to uri="{{sys:MyJvmPropertyName:log:bar}}"/> -</route> ----- -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{env:SOMENAME:log:foo}}" - - to: - uri: "{{sys:MyJvmPropertyName:log:bar}}" ----- -==== - -The boolean function is intended for more flexibility when enabling or disabling EIP options. - -For example given we have a property with key `region` that has the value `EMEA`: - -[source,properties] ----- -region = EMEA ----- - -We can then configure EIPs whether they are disabled based on this condition as follows: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .choice().disabled("{{boolean:region == 'EMEA'}}") - .when(simple("${header.RetryAttempts} == null")) - .setProperty("HttpMessageMethod", constant("SET")) - .process("SetOriginalMessageProcessor") - .end(); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <choice disabled="{{boolean:region == 'EMEA'}}"> - <when> - <simple>${header.RetryAttempts} == null</simple> - <setProperty name="HttpMessageMethod"> - <constant>SET</constant> - </setProperty> - <process ref="SetOriginalMessageProcessor"/> - </when> - </choice> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - choice: - disabled: "{{boolean:region == 'EMEA'}}" - when: - - expression: - simple: - expression: "${header.RetryAttempts} == null" - steps: - - setProperty: - name: "HttpMessageMethod" - expression: - constant: "SET" - - process: - ref: "SetOriginalMessageProcessor" ----- -==== - -[NOTE] -==== -Property placeholders can also be used in `application.properties` files, also with the functions such as ENV as shown: - -[source,properties] ----- -# server name read from ENV and fallback to use localhost as default value -myserver = {{env:MY_SERVER_NAME:localhost}} ----- -==== - -TIP: The boolean function uses the xref:components:languages:simple-language.adoc[Simple] language which has many functions and operators to build the condition. - -The service function is for looking up a service which is defined using OS environment variables using the service naming idiom, to refer to a service location using `hostname : port` - -* __NAME__**_SERVICE_HOST** -* __NAME__**_SERVICE_PORT** - -in other words the service uses `_SERVICE_HOST` and `_SERVICE_PORT` as prefix. -So if the service is named FOO, then the OS environment variables should be set as - -[source,bash] ----- -export $FOO_SERVICE_HOST=myserver -export $FOO_SERVICE_PORT=8888 ----- - -For example if the FOO service a remote HTTP service, then we can refer to the service in the Camel endpoint uri, and use the HTTP component to make the HTTP call: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("http://{{service:FOO}}/myapp"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="http://{{service:FOO}}/myapp"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "http://{{service:FOO}}/myapp" ----- -==== - -And we can use default values if the service has not been defined, for example to call a service on localhost, maybe for unit testing. - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("http://{{service:FOO:localhost:8080}}/myapp"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="http://{{service:FOO:localhost:8080}}/myapp"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "http://{{service:FOO:localhost:8080}}/myapp" ----- -==== - -The bean function (you need to have `camel-bean` JAR on classpath) is for looking up the property from the return value of bean's method. - -Assuming we have registered a bean named 'foo' that has a method called 'bar' that returns a directory name, then we can refer to the bean's method in the camel endpoint url, and use the file component to poll a directory: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("file:{{bean:foo.bar}}") - .to("direct:result"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="file:{{bean:foo.bar}}"/> - <to uri="direct:result"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: file:{{bean:foo.bar}} - steps: - - to: - uri: "direct:result" ----- -==== - -IMPORTANT: The method must be a public no-arg method (i.e. no parameters) and return a value such as a String, boolean, int. - -=== Using Kubernetes property placeholder functions - -The `camel-kubernetes` component include the following functions: - -* `configmap` - A function to lookup the string property from Kubernetes ConfigMaps. -* `configmap-binary` - A function to lookup the binary property from Kubernetes ConfigMaps. -* `secret` - A function to lookup the string property from Kubernetes Secrets. -* `secret-binary` - A function to lookup the binary property from Kubernetes Secrets. - -The syntax for both functions are: - -[source,text] ----- -configmap:name/key[:defaultValue] ----- - -Where the default value is optional, for example the following will lookup `myKey`, -and fail if there is no such configmap. - -[source,text] ----- -configmap:mymap/mykey ----- - -In this example then it would not fail as a default value is provided: - -[source,text] ----- -configmap:mymap/mykey:123 ----- - -If the value stored in the configmap is in binary format, so it is stored as `Binary Data`, it will be downloaded in a file, and it returns the absolute path of the file - -[source,text] ----- -configmap-binary:mymap/mybinkey ----- - -it returns a path like `/tmp/camel11787545916150467474/mybinkey` - -Before the Kubernetes property placeholder functions can be used they need to be configured with either (or both) - -- path - A _mount path_ that must be mounted to the running pod, to load the configmaps or secrets from local disk. -- kubernetes client - *Autowired* An `io.fabric8.kubernetes.client.KubernetesClient` instance to use for connecting to the Kubernetes API server. - -Camel will first use _mount paths_ (if configured) to lookup, and then fallback to use the `KubernetesClient`. - -==== Configuring mount paths for ConfigMaps and Secrets - -The configuration of the _mount path_ are used by the given order: - -1. Reading configuration property with keys `camel.kubernetes-config.mount-path-configmaps` and `camel.kubernetes-config.mount-path-secrets`. -2. Use JVM system property with key `camel.k.mount-path.configmaps` and `camel.k.mount-path.secrets` (Camel K compatible). -3. Use OS ENV variable with key `CAMEL_K_MOUNT_PATH_CONFIGMAPS` and `CAMEL_K_MOUNT_PATH_SECRETS` (Camel K compatible). - -For example to use `/etc/camel/resources/` as mount path, you can configure this in the `application.properties`: - -[source,properties] ----- -camel.kubernetes-config.mount-path-configmaps = /etc/camel/myconfig/ -camel.kubernetes-config.mount-path-secrets = /etc/camel/mysecrets/ ----- - -==== Configuring Kubernetes Client - -Camel will autowire the `KubernetesClient` if a single instance of the client exists in the running application (lookup via the xref:registry.adoc[Registry]). -Otherwise, a new `KubernetesClient` is created. The client can be configured from either - -- Using `camel.kubernetes-config.client.` properties (see below for example) -- Attempt to auto-configure itself by a combination of OS Environment variables, reading from `~./kube/config` configuration, -and service account token file. For more details see the https://github.com/fabric8io/kubernetes-client documentation. - -You most likely only need to explicit configure the `KubernetesClient` when you want to connect -from a local computer to a remote Kubernetes cluster, where you can specify various options, -such as the masterUrl and oauthToken as shown: - -[source,properties] ----- -camel.kubernetes-config.client.masterUrl = https://127.0.0.1:50179/ -camel.kubernetes-config.client.oauthToken = eyJhbGciOiJSUzI1NiIsImtpZCI... ----- - -TIP: The `KubernetesClient` has many options, see the https://github.com/fabric8io/kubernetes-client[Kubernetes Client] documentation for more information for these options. - -If you only use _mount paths_, then it is good practice to disable `KubernetesClient` which can be done by setting enabled to false as show: - -[source,properties] ----- -camel.kubernetes-config.client-enabled = false ----- - -When running your Camel applications inside an existing Kubernetes cluster, then you often -would not need to explicit configure the `KubernetesClient` and can rely on default settings. - -TIP: If you use Camel Quarkus, then it is recommended to use their https://quarkus.io/guides/kubernetes-config -which automatic pre-configure the `KubernetesClient` which Camel then will reuse. - -==== Using configmap with Kubernetes - -Given a configmap named `myconfig` in Kubernetes that has two entries: - -[source,properties] ----- -drink = beer -first = Carlsberg ----- - -Then these values can be used in your Camel routes such as: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("What {{configmap:myconfig/drink}} do you want?") - .log("I want {{configmap:myconfig/first}}"); ----- - -XML:: -+ -[source,xml] ----- - <route> - <from uri="direct:start"/> - <log message="What {{configmap:myconfig/drink}} do you want?"/> - <log message="I want {{configmap:myconfig/first}}"/> - </route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "What {{configmap:myconfig/drink}} do you want?" - - log: - message: "I want {{configmap:myconfig/first}}" ----- -==== - -You can also provide a default value in case a key does not exist, such as `Heiniken` being the default value: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .log("What {{configmap:myconfig/drink}} do you want?") - .log("I want {{configmap:myconfig/second:Heineken}}"); ----- - -XML:: -+ -[source,xml] ----- - <route> - <from uri="direct:start"/> - <log message="What {{configmap:myconfig/drink}} do you want?"/> - <log message="I want {{configmap:myconfig/second:Heineken}}"/> - </route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - log: - message: "What {{configmap:myconfig/drink}} do you want?" - - log: - message: "I want {{configmap:myconfig/second:Heineken}}" ----- -==== - -==== Using secrets with Kubernetes - -Camel reads ConfigMaps from the Kubernetes API Server. And when RBAC is enabled on the cluster, -the ServiceAccount that is used to run the application needs to have the proper permissions for such access. - -A secret named `mydb` could contain username and passwords to connect to a database such as: - -[source,properties] ----- -myhost = killroy -myport = 5555 -myuser = scott -mypass = tiger ----- - -This can be used in Camel with for example the Postgres Sink Kamelet: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -String sink = -""" - kamelet:postgresql-sink?serverName={{secret:mydb/myhost}}? - &serverPort={{secret:mydb/myport}} - &username={{secret:mydb/myuser}} - &password={{secret:mydb/mypass}} - &databaseName=cities - &query=INSERT INTO accounts (username,city) VALUES (:#username,:#city) -"""; - -from("direct:rome") - .setBody(constant("{ \"username\":\"oscerd\", \"city\":\"Rome\"}")) - .to(sink); ----- - -XML:: -+ -[source,xml] ----- - <route> - <from uri="direct:rome"/> - <setBody> - <constant>{ "username":"oscerd", "city":"Rome"}</constant> - </setBody> - <to uri="kamelet:postgresql-sink?serverName={{secret:mydb/myhost}} - &serverPort={{secret:mydb/myport}} - &username={{secret:mydb/myuser}} - &password={{secret:mydb/mypass}} - &databaseName=cities - &query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/> - </route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct - parameters: - name: rome - steps: - - setBody: - expression: - constant: - expression: "{ \"username\":\"oscerd\", \"city\":\"Rome\"}" - - to: - uri: kamelet - parameters: - templateId: postgresql-sink - serverName: "{{secret:mydb/myhost}}" - serverPort: "{{secret:mydb/myport}}" - username: "{{secret:mydb/myuser}}" - password: "{{secret:mydb/mypass}}" - databaseName: 'cities' - query: "INSERT INTO accounts (username,city) VALUES (:#username,:#city)" ----- -==== - -The postgres-sink Kamelet can also be configured in `application.properties` which reduces the configuration -in the route above: - -[source,properties] ----- -camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}} -camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}} -camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}} -camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}} ----- - -Which reduces the route to: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:rome") - .setBody(constant("{ \"username\":\"oscerd\", \"city\":\"Rome\"}")) - .to("kamelet:postgresql-sink?databaseName=cities&query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"); ----- - -XML:: -+ -[source,xml] ----- - <route> - <from uri="direct:rome"/> - <setBody> - <constant>{ "username":"oscerd", "city":"Rome"}</constant> - </setBody> - <to uri="kamelet:postgresql-sink?databaseName=cities - &query=INSERT INTO accounts (username,city) VALUES (:#username,:#city)"/> - </route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:rome - steps: - - setBody: - expression: - constant: - expression: "{ \"username\":\"oscerd\", \"city\":\"Rome\"}" - - to: - uri: kamelet - parameters: - templateId: postgresql-sink - databaseName: cities - query: "INSERT INTO accounts (username,city) VALUES (:#username,:#city)" ----- -==== - -==== Using configmap or secrets in local-mode - -During development, you may want to run in _local mode_ where you do not need acces to a Kubernetes cluster, to lookup the configmap. -In the local mode, then Camel will lookup the configmap _keys_ from local properties, eg: - -For example the example above with the Postgres kamelet, that was configured using a secret: - -[source,properties] ----- -camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}} -camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}} -camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}} -camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}} ----- - -Now suppose we have a local Postgres database we want to use, then we can turn on _local mode_ -and specify the credentials in the same properties file: - -[source,properties] ----- -camel.kubernetes-config.local-mode = true -mydb/myhost=localhost -mydb/myport=1234 -mydb/myuser=scott -mydb/mypass=tiger ----- - -NOTE: Notice how the key is prefixed with the name of the secret and a slash, eg `name/key`. This makes it easy to copy/paste -from the actual use of the configmap/secret and into the `application.properties` file. - - -=== Using custom property placeholder functions - -The xref:components::properties-component.adoc[Properties] component allow to plugin 3rd party functions which can be used during parsing of the property placeholders. -These functions are then able to do custom logic to resolve the placeholders, such as looking up in databases, do custom computations, or whatnot. -The name of the function becomes the prefix used in the placeholder. - -This is best illustrated in the example route below, where we use `beer` as the prefix: - -[tabs] -==== - -Java:: -+ -[source,java] ----- -from("direct:start") - .to("{{beer:FOO}}") - .to("{{beer:BAR}}"); ----- - -XML:: -+ -[source,xml] ----- -<route> - <from uri="direct:start"/> - <to uri="{{beer:FOO}}"/> - <to uri="{{beer:BAR}}"/> -</route> ----- - -YAML:: -+ -[source,yaml] ----- -- route: - from: - uri: direct:start - steps: - - to: - uri: "{{beer:FOO}}" - - to: - uri: "{{beer:BAR}}" ----- -==== - -The implementation of the function is only two methods as shown below: - -[source,java] ----- [email protected]("beer") -public class MyBeerFunction implements PropertiesFunction { - - @Override - public String getName() { - return "beer"; - } - - @Override - public String apply(String remainder) { - return "mock:" + remainder.toLowerCase(); - } -} ----- - -The function must implement the `org.apache.camel.spi.PropertiesFunction` interface. -The method `getName` is the name of the function (beer). -And the `apply` method is where we implement the custom logic to do. -As the sample code is from a unit test, it just returns a value to refer to a mock endpoint. - -You also need to have `camel-component-maven-plugin` Maven plugin as part of building the component will -then ensure that this custom properties function has necessary source code generated that makes Camel -able to automatically discover the function. - -NOTE: If the custom properties function need logic to startup and shutdown, then the function can extend `ServiceSupport` -and have this logic in `doStart` and `doStop` methods. - -TIP: For an example see the `camel-base64` component. +Camel supports built-in functions for looking up properties from environment variables, system properties, +Kubernetes, and custom sources. +See xref:property-placeholder-functions.adoc[Property Placeholder Functions] for details. == Using third party property sources
