Tag Archives: Apache Camel

Apache Camel OpenAPI Contract-First Example in SpringBoot

Contract-First means what the name implies that we write the contract between client and server first and implement the code for it after. This is a good way to break up the task of creating an API in smaller parts which can be handled over multiple professions. For example, the OpenAPI specification can be created by an IT-architect and the implementation can be done by a programmer, and lastly the API documentation can be carried out by an communications expert.

In this article I’m going to build a simple OpenAPI implementation in Apache Camel and it’s rest-openapi component in a SpringBoot application.

1. We start with the API Specification:

openapi: 3.0.3
info:
  title: Basic API
  version: "1.0"
paths:
  /test:
    get:
      operationId: test
      responses:
        200:
          description: Default response
  /user/{userId}:
    get:
      operationId: getUser
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        200:
          description: Default response

A few things to note here:
/test – this is the url that the client will use
operationId for the test endpoint – is the route that will receive the call from url above
/user/{userId} – url with parameter that the client will use
operationId – here the operationId does not match the url, which is fine. The call will go to the route direct:getUser with the userId in a header on the message seen below

2. Implementation of the Camel solution

package com.example.contract_first_example;


import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;


/**
 * A Camel Java DSL Router for Contract First Example
 */
@Component
public class MyApp extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        rest().openApi().specification("hello-rest-service.yaml");

        from("direct://hello")
            .setBody().constant("Hello from Camel!");

        from("direct://getUser")
            .process(exchange -> {
                exchange.getMessage().setBody("Hello from user: " 
                             + exchange.getMessage().getHeader("userId"));
            });
    }
}

3. Now the implementation is done and we move on to testing:

PS C:\Users\niklas> curl -XGET localhost:8080/hello
Hello from Camel!
PS C:\Users\niklas> curl -XGET localhost:8080/user/11
Hello from user: 11

And that is all there is – pretty simple, like most things in the Camel world ๐Ÿ˜‰

Tested on Java 17, OpenAPI 3.0, Apache Camel 4.9.0 and SpringBoot 3.3.4 in a Windows 10 environment

Apache Camel CXF gives you “org.apache.cxf.service.factory.ServiceConstructionException: Could not find portType named {<some namespace>}<some service>PortType”

I got this when implementing a SOAP service from a provided wsdl. I hope I would not done the same mistake if I wrote the wsdl myself, but we will never know for sure ๐Ÿ˜‰

Now to the solution. You probably have something like this in your code (Apache Camel in a SpringBoot application)

...
@Component
public class CurrencyRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("cxf:bean:currencyLookupAdapterEndpoint")
            .log("Body: ${body}");
    }

    @Bean
    private CxfEndpoint currencyLookupAdapterEndpoint() {
        final CxfEndpoint cxfEndpoint = new CxfEndpoint();
        cxfEndpoint.setWsdlURL("currencies.wsdl");
        cxfEndpoint.setAddress("/getCurrencies");

        // Set the Service Class
        cxfEndpoint.setServiceClass(CurrenciesResponderService.class);

        cxfEndpoint.setProperties(new HashMap<>());
        cxfEndpoint.getProperties().put("schema-validation-enabled", "true");
        return cxfEndpoint;
    }
}

In the currencies.wsdl I had a CurrenciesResponderService and a CurrenciesResponderInterface.
If I choose the CurrenciesResponderService.class as the ServiceClass I got the error below:

org.apache.cxf.service.factory.ServiceConstructionException: Could not find portType named {<some namespace>}<some service>PortType

and if I choose the CurrenciesResponderInterface.class instead the application started without the error ๐Ÿ™‚

Tested on Apache Camel v3.17 and SpringBoot v3.2.0

Camel-K: Add multiple custom classes via .jar

I came across a use case where we needed a MapForce mapping in our Camel-K flow. MapForce generated Java code consists of many classes and it becomes overly cumbersome to add them all to the kamel run command. To solve this problem we put all the MapForce generated code into a .jar file and then added it to our cluster and referenced it in our kamel run command

1. First we made a .jar file out of the generated java code. For this we use the jar command that can be found in most java developer kits

jar cvf MapFormatAToFormatB.jar com/ se/

This creates a .jar file called MapFormatAToFormatB.jar with the contents of the generated code com/ and se/. Mapforce puts general classes into com/ package and our custom classes into your own packages, in our case se/.

2. Create a ConfigMap to hold our .jar so that Camel-K-Operator can use it

kubectl create configmap map-formata-to-formatb --from-file=MapFormatAToFormatB.jar

3. Reference the configmap in you kamel run command and add a trait to put it on the classpath

kamel run src/main/java/myApp --resource configmap:map-formata-to-formatb -t jvm.classpath=/etc/camel/resources/map-formata-to-formatb/MapFormatAToFormatB.jar

Thatยดs it. In our code we reference these classes the same way we should have if the where part of our code base

import com.altova.io.Input;
import com.altova.io.StringInput;
import com.altova.io.StringOutput;
import se.myorg.integration.MapFormatAToFormatB;

import org.apache.camel.builder.RouteBuilder;

public class AmbuReg extends RouteBuilder {
    @Override
    public void configure() throws Exception {
       ...
    }
}

Tested on Camel-K-Operator v1.11.1 and Kubernetes v1.24.11