Category Archives: Java - Page 2

JUnit 5 assert log entries in a SpringBoot application

Asserting log entries in JUnit Jupiter is not as straight forward as one might want, but after some digging I found a way that worked for me. It should also me said that doing this i Jupiter is far easier than in previous versions of JUnit

The trick here is to use the SpringBoot OutputCaptureExtension on you test class. This can be accomplished with the @ExtendWith annotation in SpringBoot. This will inject a CapturedOutput object with output from our application

Example test class:

...    
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.containsString;

@Test
@ExtendWith(OutputCaptureExtension.class)
public void testHappyCaseSendDataToSystem(CapturedOutput output) throws Exception {

  MockEndpoint mock = camelContext.getEndpoint("mock:senddataToSystem", MockEndpoint.class);

  // Setting expectations
  mock.expectedMessageCount(1);

  // Invoking consumer
  producerTemplate.sendBody("direct:start", null);

  // Asserting mock is satisfied
  mock.assertIsSatisfied();

  // Assert log message
  assertThat(output.getOut(), containsString("LogMessage=Completed"));
}

Tested on SpringBoot v3.2.0 and JUnit 5.10.1

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

Camel-K: Custom HTTP Client with Kerberos auth

I haven’t been able to do this using the Apache Camel HTTP component yet, so I put together a Processor to do it for me instead. To make this work we need a krb5.conf and login.conf file with settings for our domain.
Example krb5.conf

[libdefaults]
    default_realm = MYDOMAIN.SE

[realms]
    MYDOMAIN.SE = {
        kdc = kdc.mydomain.se
    }

Example Login.conf

com.sun.security.jgss.krb5.initiate {
  com.sun.security.auth.module.Krb5LoginModule required
 doNotPrompt=false useTicketCache=true;
};

Camel flow:

public class MyFlow extends RouteBuilder {

  @Override
  public void configure() throws Exception {
    from("timer:myTimer?repeatCount=1")
          .process(new CustomHTTPClient())
          .log("${body}");                  
  }

  // Create a custom Authenticator
  static class MyAuthenticator extends Authenticator {
    public PasswordAuthentication getPasswordAuthentication() {
      return (new PasswordAuthentication("username", 
                                         "password".toCharArray()));
    }
  }

  // Camel Processor
  public static class CustomHTTPClient implements Processor {

    public void process(Exchange exchange) throws Exception {
      // Setup Kerberos authentication via Java VM options
      System.setProperty("java.security.krb5.conf", "/etc/krb5.conf");
      System.setProperty("java.security.auth.login.config", "/etc/login.conf");
      System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
      System.setProperty("sun.security.krb5.debug", "true"); // Debug logging 

      // Set a custom Authenticator
      Authenticator.setDefault(new MyAuthenticator());

      // Custom "HTTPClient"
      URL url = new URL("https://resource.mydomain.se/data");
      InputStream ins = url.openConnection().getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
      String str = "";
      for (String line; (line = reader.readLine()) != null; str += line);

      // Set payload as message back to flow
      exchange.getIn().setBody(str);
    }
  }
}

Kamel run arguments:

kamel run MyFlow.java 
               --resource file:krb5.conf@/etc/krb5.conf 
               --resource file:login.conf@/etc/login.conf

Tested on Apache Camel K Runtime 1.16.0, Apache Camel 3.19.0, Minikube v1.29.0 and WSL2 Ubuntu 20.04.4 LTS