Property placeholders
Camel has extensive support for property placeholders, which can be used almost anywhere in your Camel routes, endpoints, DSL, and route configuration, 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 Properties out of the box from the core, which is responsible for handling and resolving the property placeholders.
See the 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:
{{file.uri}}
where file.uri
is the property key.
Property placeholders can be used to specify parts, or all, of an endpoint’s URI by embedding one or more placeholders in the URI’s string definition.
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:
{{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 defined with the ?
(question mark) as prefix in the key name, as shown:
{{?myBufferSize}}
If a value for the key exists then the value is used, however if the key does not exists, then Camel understands this, such as when used in Endpoints:
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 restructued as:
file:foo
Then the option bufferSize
is not in specified at all, 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.
integration.ftpEnabled=true
from("ftp:....").autoStartup("{{integration.ftpEnabled}}")
.to("kafka:cheese")
from("jms:....").autoStartup("{{!integration.ftpEnabled}}")
.to("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 disable, and vise-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 URIs you should use this with the syntax {{key}}
as shown in this example:
cool.end = mock:result
where = cheese
And in Java DSL:
from("direct:start")
.to("{{cool.end}}");
And in XML DSL:
<route>
<from uri="direct:start"/>
<to uri="{{cool.end}}"/>
</route>
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:
buf = 8192
from("direct:start")
.to("file:outbox?bufferSize={{buf}}");
And in XML DSL:
<route>
<from uri="direct:start"/>
<to uri="file:outbox?bufferSize={{buf}}"/>
</route>
However the placeholder can be anywhere, so it could also be the name of a mock endpoint
from("direct:start")
.to("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
You can also have properties with refer to each other such as:
cool.foo=result
cool.concat=mock:{{cool.foo}}
Notice how cool.concat
refer to another property.
And the route in XML:
<route>
<from uri="direct:start"/>
<to uri="{{cool.concat}}"/>
</route>
Using property placeholders multiple times
You can of couse also use placeholders several times:
cool.start=direct:start
cool.showid=true
cool.result=result
And in this route we use cool.start
two times:
from("{{cool.start}}")
.to("log:{{cool.start}}?showBodyType=false&showExchangeId={{cool.showid}}")
.to("mock:{{cool.result}}");
Using property placeholders with producer template
You can also your property placeholders when using ProducerTemplate for example:
template.sendBody("{{cool.start}}", "Hello World");
Using property placeholders with consumer template
This can also be done when using ConsumerTemplate, such as:
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 thePropertiesComponent
to resolve a single property from Java code. -
Use the method
resolvePropertyPlaceholders
on theCamelContext
to resolve (one or more) property placeholder(s) in a String.
For example to resolve a placeholder with key foo, you can do:
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.
Lets try with an example to explain this:
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:
greeting = Hi
cool = awesome
Will be resolved to:
Hi Camel user, Camel is awesome dont you think?
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 Multicast EIP. The stopOnException
is a xs:boolean
type,
so we cannot configure this as:
<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:
<multicast prop:stopOnException="stop">
...
</multicast>
The complete example is below:
<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:
stop = true
Bridging Camel property placeholders with Spring XML files
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:
<!-- 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:
<!-- 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
The 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 -
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:
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{env:SOMENAME}}"/>
<to uri="{{sys:MyJvmPropertyName}}"/>
</route>
</camelContext>
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.
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{env:SOMENAME:log:foo}}"/>
<to uri="{{sys:MyJvmPropertyName:log:bar}}"/>
</route>
</camelContext>
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
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:
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="http://{{service:FOO}}/myapp"/>
</route>
</camelContext>
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.
<camelContext>
<route>
<from uri="direct:start"/>
<to uri="http://{{service:FOO:localhost:8080}}/myapp"/>
</route>
</camelContext>
Using custom property placeholder functions
The 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 code below with Spring XML files:
<beans>
<bean id="beerFunction" class="MyBeerFunction"/>
<camelContext>
<propertyPlaceholder id="properties">
<propertiesFunction ref="beerFunction"/>
</propertyPlaceholder>
<route>
<from uri="direct:start"/>
<to uri="{{beer:FOO}}"/>
<to uri="{{beer:BAR}}"/>
</route>
</camelContext>
</beans>
Here we have a Camel Spring XML route where we have defined the
<propertyPlaceholder>
to use a custom function, which we refer to be the bean id (beerFunction
).
As the beer function uses "beer"
as its name, then the placeholder syntax can trigger the beer function by starting with beer:value
.
The implementation of the function is only two methods as shown below:
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.
To register a custom function from Java code is as shown below:
PropertiesComponent pc = context.getPropertiesComponent();
pc.addFunction(new MyBeerFunction());
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 automatic be discovered from classpath when Camel is starting up.
This is done by include the file META-INF/services/org/apache/camel/property-source-factory
file which refers to the fully qualified class name of the PropertySource
implementation.
See MicroProfile Config component as an example.
You can also register 3rd-party property sources via Java API:
PropertiesComponent pc = context.getPropertiesComponent();
pc.addPropertySource(myPropertySource);