java.lang.IllegalArgumentException: CamelContext must be specified on: Message[] - Camel core - maven

Below is builder from camel core & writting junit testcases with it & camel core version used is 2.22.1.
new ExchangeBuilder(null)
.withBody(body)
.withHeader(header, headerValue)
.build();
Junit testcases is throwing error when calling above builder - java.lang.IllegalArgumentException: CamelContext must be specified on: Message[]

You'll have to provide constructor of ExchangeBuilder a instance of CamelContext. If your test class inherits from CamelTestBuilder you can use context() method to obtain it during a test.
public class SomeTest extends CamelTestSupport {
#Test
public void testSomething(){
String body = "some body";
String header = "SomeHeader";
String headerValue = "Some header value";
ExchangeBuilder builder = new ExchangeBuilder(context())
.withBody(body).withHeader(header, headerValue);
Exchange exchange = builder.build();
CamelContext contextFromExchange = exchange.getContext();
// Do something with the exchange?
}
}
If you're not using one of the camel-test modules you'll have to create and configure one manually using e.g new DefaultCamelContext();. For testing routes use of one of the camel-test modules is highly recommended though as they make things a lot easier.

Related

JUnit tests in Spring Boot 3.0 does not work

Code
GET method inside controller class:
#GetMapping("/getAllRecommendedMovies")
public ResponseEntity<BookMyTicket> getAllRecommendedMovies(
#RequestParam(value = "theatreName", required = false) String theatreName,
#RequestParam(value = "pincode", required = false) Integer pincode,
HttpServletRequest request) {
return Observation.createNotStarted(
request.getRequestURI().substring(1),
observationRegistry
).observe(() -> new ResponseEntity<(
theatreManagementService.getAllRecommendedMovies(theatreName, pincode),
HttpStatus.OK
));
}
JUnit test:
#Test
public void getAllRecommendedMovies() throws Exception {
try (MockedStatic<Observation> utilities = Mockito.mockStatic(Observation.class)) {
utilities.when(
() -> Observation.createNotStarted(Mockito.eq("getAllRecommendedMovies"), Mockito.any())
).thenReturn(Observation.NOOP);
}
mockMvc.perform(get("/getAllRecommendedMovies")).andExpect(status().isOk());
}
Also on Github: TheatreManagementControllerTest.java
Question
I have implemented JUnit test for ObservationRegistry.
Is there any alternate method to implement?
Mockito.mockStatic can only mock static calls that happen in the same thread. See https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks.
Spring MVC tests run the Spring application in its own thread so static mocking with Mockito won't help here.
I suggest you introduce a ObservationService interface to wrap methods like Observation.createNotStarted(..) and use that service in your controller. The service can then be easily mocked using standard Spring testing mechanisms like #MockBean.

Not able to register JavaTimeModule

I have a spring boot application with version 2.7.3.
I have a util class, that creates a new Object mapper and registers the Javatimemodule class to that.
So in the test case, I try to verify that the registered module is added to the mapper.
This was completely working fine with my previous spring version 2.3. However when upgrading I see this strange issue.
Can some one help here ?
static {
mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
public static ObjectMapper getObjectMapper() {
return mapper;
}
#Test
public void returnsObjectMapper() {
ObjectMapper objectMapper = Util.getObjectMapper();
Set<Object> registeredModuleIds = objectMapper.getRegisteredModuleIds();
assertThat(registeredModuleIds).contains(JavaTimeModule.class.getName());
}
So when I run the test case with the higher version, I get the following error
java.lang.AssertionError: Expecting UnmodifiableSet:
["jackson-datatype-jsr310"] to contain:
["com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"] but could not
find the following element(s):
["com.fasterxml.jackson.datatype.jsr310.JavaTimeModule"]
Judging by the log, ObjectMapper actually has a required module registered in your test, but it's called "jackson-datatype-jsr310" instead of the class name.
AFAIK spring-boot of 2.7.3 uses Jackson version 2.13.3, where JavaTimeModule's default constructor calls super constructor (of SimpleModule class) with a name "jackson-datatype-jsr310", and this name is returned by overridden SimpleModule's getTypeId() method, which is stored then in the ObjectMapper's _registeredModuleIds map.
This name is picked from the com.fasterxml.jackson.datatype.jsr310.PackageVersion static VERSION variable, so if you want your test to pass try something like this:
#Test
public void returnsObjectMapper() {
ObjectMapper objectMapper = Util.getObjectMapper();
String javaTimeModuleName = PackageVersion.VERSION.getArtifactId();
Set<Object> registeredModuleIds = objectMapper.getRegisteredModuleIds();
assertThat(registeredModuleIds).contains(javaTimeModuleName);
}
Mind that exactly com.fasterxml.jackson.datatype.jsr310.PackageVersion must be imported, because every Jackson package has this class.

Dynamic to() in Apache Camel Route

I am writing a demo program using Apache Camel. Out Camel route is being called from a Spring Boot scheduler and it will transfer file from the source directory C:\CamelDemo\inputFolder to the destination directory C:\CamelDemo\outputFolder
The Spring Boot scheduler is as under
#Component
public class Scheduler {
#Autowired
private ProducerTemplate producerTemplate;
#Scheduled(cron = "#{#getCronValue}")
public void scheduleJob() {
System.out.println("Scheduler executing");
String inputEndpoint = "file:C:\\CamelDemo\\inputFolder?noop=true&sendEmptyMessageWhenIdle=true";
String outputEndpoint = "file:C:\\CamelDemo\\outputFolder?autoCreate=false";
Map<String, Object> headerMap = new HashMap<String, Object>();
headerMap.put("inputEndpoint", inputEndpoint);
headerMap.put("outputEndpoint", outputEndpoint);
producerTemplate.sendBodyAndHeaders("direct:transferFile", null, headerMap);
System.out.println("Scheduler complete");
}
}
The Apache Camel route is as under
#Component
public class FileTransferRoute extends RouteBuilder {
#Override
public void configure() {
errorHandler(defaultErrorHandler()
.maximumRedeliveries(3)
.redeliverDelay(1000)
.retryAttemptedLogLevel(LoggingLevel.WARN));
from("direct:transferFile")
.log("Route reached")
.log("Input Endpoint: ${in.headers.inputEndpoint}")
.log("Output Endpoint: ${in.headers.outputEndpoint}")
.pollEnrich().simple("${in.headers.inputEndpoint}")
.recipientList(header("outputEndpoint"));
//.to("file:C:\\CamelDemo\\outputFolder?autoCreate=false")
}
}
When I am commenting out the line for recipientList() and uncommenting the to() i.e. givig static endpoint in to(), the flow is working. But when I am commenting to() and uncommenting recipientList(), it is not working. Please help how to route the message to the dynamic endpoint (outputEndpoint)?
You are using pollEnrich without specifying an AggregationStrategy: in this case, Camel will create a new OUT message from the retrieved resource, without combining it to the original IN message: this means you will lose the headers previously set on the IN message.
See documentation : https://camel.apache.org/manual/latest/enrich-eip.html#_a_little_enrich_example_using_java
strategyRef Refers to an AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message.
A simple solution would be to define a simple AggregationStrategy on your pollEnrich component, which simply copies headers from the IN message to the new OUT message (note that you will then use the original IN message body, but in your case it's not a problem I guess)
from("direct:transferFile")
.log("Route reached")
.log("Input Endpoint: ${in.headers.inputEndpoint}")
.log("Output Endpoint: ${in.headers.outputEndpoint}")
.pollEnrich().simple("${in.headers.inputEndpoint}")
.aggregationStrategy((oldExchange, newExchange) -> {
// Copy all headers from IN message to the new OUT Message
newExchange.getIn().getHeaders().putAll(oldExchange.getIn().getHeaders());
return newExchange;
})
.log("Output Endpoint (after pollEnrich): ${in.headers.outputEndpoint}")
.recipientList(header("outputEndpoint"));
//.to("file:C:\\var\\CamelDemo\\outputFolder?autoCreate=false");

Unable to generate the Spring rest docs using Cucumber

I am trying to test spring rest documentation for rest API for our services using spring cucumber jvm but end up with a null pointer exeception when I try to execute the scenario, as the framework is not able to intialize the Junit context.
Error Message:
java.lang.NullPointerException at
org.springframework.restdocs.ManualRestDocumentation.beforeO‌​peration(ManualRestD‌​ocumentation.java:90‌​) at
org.springframework.restdocs.JUnitRestDocumentation.beforeOp‌​eration(JUnitRestDoc‌​umentation.java:76)
Code:
private AppProperties props;
#Before("#rest") public void beforeScenario() {
JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation( "target/generated-snippets" );
System.out.println( "jUnitRestDocumentation " +restDocumentation );
spec = new RequestSpecBuilder().addFilter( documentationConfiguration( restDocumentation ) ).build();
System.out.println( "\n spec init .. " +restDocumentation );
}
Step definition code:
#Given("^create a rest document for VHR API$")
public void create_a_rest_document_for_VHR_API() throws Throwable {
estAssured.given( spec )
.accept( "application/json" )
.filter( document( "vhrdocument" ) ) .when()
.get( props.getVhrrequesturl() + "/vhrData/{vehicleID}", "5VW4T7AU0FM029999" ) .then().log().all();
}
You aren't using JUnitRestDocumentation as it's intended to be used. It's designed to be used as a JUnit rule which means it should be a public field annotated with #Rule:
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
Being a rule means that JUnit will automatically call restDocumentation for each test, allowing Spring REST Docs to set up and tear down the test-specific context. The NullPointerException is occurring because restDocumentation hasn't been called in this way and, therefore, the context hasn't been set up.
You haven't described how you're using Cucumber, but if you're using it's JUnit runner you should be able to fix the problem by declaring restDocumentation as a #Rule-annotated field as shown above. If you're not using its JUnit runner, you may need to use Spring REST Docs' ManualRestDocumentation instead. The Spring REST Docs reference documentation contains a section that describes how to set up your tests when you're not using JUnit.
I had the same problem because I had multiple test class inheriting the class, in which I declared the JUnitRestDocumentation instance. My mistake was that I declared the rule using the #Rule annotation. I should have used #ClassRule and declared the instance as static.
#ClassRule
public static JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
it happened with test SpockFramework, and i added to pom.xml:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-junit4</artifactId>
<scope>test</scope>
</dependency>
I had the same symptoms when migrating from RestAssured 2.x to RestAssured 3.1.1.
The codebase had a way to setup RestAssured in order to avoid repetitive ceremony for every tests :
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
#Before
public void configure_rest_assured() {
RestAssured.port = springServerPort;
RestAssured.config = config().objectMapperConfig(
objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
)
...;
RestAssured.requestSpecification = new RequestSpecBuilder()
.addRequestSpecification(documentationConfiguration(docRule, ...))
...
.build();
}
This was working well, until I migrated to 3.x. The issue was that new RequestSpecBuilder() will append itself to the default static RestAssured.requestSpecification.
The first test passed, but when it finished the rule was disposed (the after part), when the second test started to ran, the Before method was chaining
the specification created for the first test (referencing the disposed rule used by the first test method)
the specification created for the second test (referencing the active rule for second test method)
And so on as new tests are ran.
But when the second test is run RestAssured invoke specification in order, e.g. the number 1, but since it was referencing a disposed rule (the beforeOperation was executed on a null context)
To fix that the code had to clear the previous specifications :
#Before
public void configure_rest_assured() {
RestAssured.port = springServerPort;
RestAssured.config = config().objectMapperConfig(
objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
)
...;
RestAssured.requestSpecification = null; // avoid the builder to acquire previous specs.
RestAssured.requestSpecification = new RequestSpecBuilder()
.addRequestSpecification(documentationConfiguration(docRule, ...))
...
.build();
}
For using cucumber-java-8 with spring rest docs and spring-security the following worked for me.
This is combining #AndyWilkison's answer from above but using the cucumber hooks instead of junit rules.
public class StepDefs implements En {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
private ManualRestDocumentation restDocumentation = new ManualRestDocumentation();
public StepDefs() {
BeforeStep((Scenario scenario) -> {
restDocumentation.beforeTest(AuthenticationStepDefs.class, scenario.getName());
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).apply(documentationConfiguration(restDocumentation)).build();
});
AfterStep((Scenario scenario) -> {
restDocumentation.afterTest();
});
When("create a rest document for VHR API", () -> {
MvcResult result = mockMvc.perform(/*
your normal call here
*/).
.andDo(document("documentation")).
.andReturn();
}
}
}

how to change the #FeignClient name in runtime

I use Spring Cloud Netflix to build my micro service .
#FeignClient(name = "ms-cloud",configuration = MsCloudClientConfig.class)
public interface TestClient {
/**
* #return
*/
#RequestMapping(value = "/test", method = RequestMethod.GET)
String test();
}
I want to change the name to ms-cloud-pre when some special user.
Anyone can give some advice?
According to the documentation feign supports placeholders in the name and url fields.
#FeignClient(name = "${store.name}")
public interface StoreClient {
//..
}
So you could set store.name=storeProd at runtime using normal spring boot configuration mechanisms.
To create a spring-cloud Feign client at runtime in situations where you don't know the service-id until the point of call:
import org.springframework.cloud.openfeign.FeignClientBuilder;
#Component
public class InfoFeignClient {
interface InfoCallSpec {
#RequestMapping(value = "/actuator/info", method = GET)
String info();
}
FeignClientBuilder feignClientBuilder;
public InfoFeignClient(#Autowired ApplicationContext appContext) {
this.feignClientBuilder = new FeignClientBuilder(appContext);
}
public String getInfo(String serviceId) {
InfoCallSpec spec =
this.feignClientBuilder.forType(InfoCallSpec.class, serviceId).build();
return spec.info();
}
}
That actually is possible. In Spring Cloud Zookeeper we're doing a similar thing since the name of the service in the Feign client is not the one that is there in the in Zookeeper. It can be an alias presented in the yaml file. Here you have the code example https://github.com/spring-cloud/spring-cloud-zookeeper/blob/master/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/dependency/DependencyRibbonAutoConfiguration.java#L54 and here you have the description of the dependencies feature - https://github.com/spring-cloud/spring-cloud-zookeeper/blob/master/docs/src/main/asciidoc/spring-cloud-zookeeper.adoc#using-the-zookeeper-dependencies

Resources