Customizers allow you to modify integrations based on the target product (Camel Quarkus, Camel Spring Boot, or CXF Quarkus). This pattern enables you to write product-agnostic integration tests while handling product-specific configurations.
Why use customizers?
When your integration needs to run on multiple products with different configurations, customizers provide a clean way to handle these differences without duplicating test code.
Customizers are executed during integration generation. They have access to the IntegrationBuilder and can modify route builders, properties, dependencies, and more.
Customizer types
TNB provides several customizer base classes:
Base customizer
The abstract Customizer class is the foundation:
package software.tnb.product.customizer;
import software.tnb.product.integration.builder.AbstractIntegrationBuilder;
public abstract class Customizer {
private ProductType product;
private AbstractIntegrationBuilder<?> integrationBuilder;
public abstract void customize();
public AbstractIntegrationBuilder<?> getIntegrationBuilder() {
return integrationBuilder;
}
}
Product-specific customizers
Extend these classes to create customizers for specific products:
import software.tnb.product.cq.customizer.QuarkusCustomizer;
public class MyQuarkusCustomizer extends QuarkusCustomizer {
@Override
public void customize() {
getIntegrationBuilder()
.addToApplicationProperties("quarkus.http.port", "8080")
.dependencies("quarkus-rest");
}
}
import software.tnb.product.csb.customizer.SpringBootCustomizer;
public class MySpringBootCustomizer extends SpringBootCustomizer {
@Override
public void customize() {
getIntegrationBuilder()
.addToApplicationProperties("server.port", "8080")
.dependencies("spring-boot-starter-web");
}
}
ProductsCustomizer
Use ProductsCustomizer when you need different behavior for two or more products:
import software.tnb.product.customizer.ProductsCustomizer;
public class MyProductsCustomizer extends ProductsCustomizer {
@Override
public void customizeQuarkus() {
getIntegrationBuilder()
.addToApplicationProperties("quarkus.camel.servlet.url-patterns", "/camel/*")
.dependencies("rest");
}
@Override
public void customizeSpringboot() {
getIntegrationBuilder()
.addToApplicationProperties("camel.servlet.mapping.context-path", "/camel/*")
.dependencies("camel-servlet");
}
}
Using the Customizers enum
The Customizers enum provides a convenient way to create inline customizers:
import software.tnb.product.customizer.Customizers;
import software.tnb.product.integration.builder.IntegrationBuilder;
IntegrationBuilder ib = new IntegrationBuilder("my-integration")
.fromRouteBuilder(routeBuilder)
.addCustomizer(
Customizers.QUARKUS.customize(builder -> {
builder.addToApplicationProperties("quarkus.log.level", "DEBUG");
}),
Customizers.SPRINGBOOT.customize(builder -> {
builder.addToApplicationProperties("logging.level.root", "DEBUG");
})
);
This approach is cleaner than creating separate customizer classes for simple modifications.
Built-in component customizers
TNB includes pre-built customizers for common Camel components. These handle product-specific configuration automatically.
REST customizer
Configures REST endpoints with proper servlet mappings:
import software.tnb.product.customizer.component.rest.RestCustomizer;
IntegrationBuilder ib = new IntegrationBuilder("rest-example")
.fromRouteBuilder(new RouteBuilder() {
@Override
public void configure() throws Exception {
rest("/api")
.get("/hello").to("direct:hello");
from("direct:hello")
.setBody(constant("Hello World"));
}
})
.addCustomizer(new RestCustomizer());
The RestCustomizer implementation:
public class RestCustomizer extends ProductsCustomizer {
private static final String DEFAULT_PATH = "/camel";
private final String path;
public RestCustomizer() {
this.path = DEFAULT_PATH;
}
public RestCustomizer(String path) {
this.path = path;
}
@Override
public void customizeQuarkus() {
getIntegrationBuilder()
.addToApplicationProperties("quarkus.camel.servlet.url-patterns", path + "/*")
.addToApplicationProperties("quarkus.openshift.route.expose", "true")
.dependencies("rest");
}
@Override
public void customizeSpringboot() {
if (!OpenshiftConfiguration.isOpenshift()) {
getIntegrationBuilder()
.dependencies(
Maven.createDependency(
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-tomcat"
)
)
.dependencies(
Maven.createDependency("org.springframework.boot:spring-boot-starter-undertow")
);
}
}
}
Other built-in customizers
TNB includes customizers for various components:
DataSourceCustomizer
Database connection configuration
PahoCustomizer
MQTT client configuration (Paho v3)
PahoCustomizerMQTT5
MQTT v5 client configuration
MongoDBCustomizer
MongoDB component configuration
CxfSoapCustomizer
CXF SOAP web service configuration
CxfRestCustomizer
CXF REST web service configuration
MllpCustomizer
HL7 MLLP component configuration
SagaLRACustomizer
Saga LRA pattern configuration
AnnotatedBeanCustomizer
Bean component with annotations
CryostatCustomizer
JFR profiling configuration
BytemanCustomizer
Byteman instrumentation
Accessing route builder code
Customizers can modify the route builder’s AST (Abstract Syntax Tree) using JavaParser:
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import software.tnb.product.customizer.Customizer;
public class RouteModifyingCustomizer extends Customizer {
@Override
public void customize() {
// Get the route builder class by name
ClassOrInterfaceDeclaration clazz = getRouteBuilderClass("MyRouteBuilder");
// Get the configure() method
MethodDeclaration configureMethod = getConfigureMethod("MyRouteBuilder");
// Modify the AST
// ... JavaParser modifications ...
}
}
Customizer execution
Customizers are executed during integration generation:
Integration builder configured
You create an IntegrationBuilder and add customizers.
Product creates integration
The Product calls createIntegrationApp(integrationBuilder).
Customizers execute
Each customizer’s doCustomize() method is called, which:
- Checks if the customizer applies to the current product
- Calls the
customize() method if applicable
Integration generated
The modified integration is generated with all customizations applied.
Example: Multi-product HTTP configuration
Here’s a complete example configuring HTTP servers differently for each product:
import software.tnb.product.customizer.ProductsCustomizer;
import software.tnb.product.integration.builder.IntegrationBuilder;
import org.apache.camel.builder.RouteBuilder;
public class HttpTest {
@Test
public void testHttpEndpoint() {
IntegrationBuilder ib = new IntegrationBuilder("http-test")
.fromRouteBuilder(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("platform-http:/hello")
.setBody(constant("Hello World"));
}
})
.addCustomizer(new HttpConfigCustomizer())
.port(8080);
product.createIntegration(ib);
// Test the endpoint
String response = RestAssured.get("/hello").asString();
assertEquals("Hello World", response);
}
static class HttpConfigCustomizer extends ProductsCustomizer {
@Override
public void customizeQuarkus() {
getIntegrationBuilder()
.dependencies("platform-http")
.addToApplicationProperties("quarkus.http.port", "8080")
.addToApplicationProperties("quarkus.http.host", "0.0.0.0");
}
@Override
public void customizeSpringboot() {
getIntegrationBuilder()
.dependencies("platform-http", "spring-boot-starter-web")
.addToApplicationProperties("server.port", "8080")
.addToApplicationProperties("camel.component.platform-http.spring-boot-integration", "true");
}
}
}
POM customizers
For advanced Maven POM modifications, use POMCustomizer:
import software.tnb.product.customizer.POMCustomizer;
import org.apache.maven.model.Model;
public class MyPOMCustomizer extends POMCustomizer {
@Override
public void customizePom(Model model) {
// Modify the Maven model directly
model.setName("Custom Integration Name");
model.setDescription("Modified via customizer");
// Add build configuration
// ... modify model ...
}
}
Best practices
Use Customizers enum for simple cases
For simple property or dependency changes, use Customizers.QUARKUS.customize() or Customizers.SPRINGBOOT.customize() instead of creating separate classes.
Create reusable customizer classes
When the same customization is needed across multiple tests, create a reusable customizer class.
Extend ProductsCustomizer for multi-product support
When an integration must work on both Quarkus and Spring Boot with different configurations, extend ProductsCustomizer.
Keep customizers focused
Each customizer should have a single responsibility (e.g., HTTP configuration, database setup).
Document product differences
Add comments explaining why different products need different configurations.