Object distortion while passing from REST service to Spring app - spring

I've got strange problem and I hope you will to help me to solve it.
I try to pass list of objects, where each object contains LocalDate parameter (JodaTime library) from test service to my controller.
This is method from my service. It returns list of objects. Look at the dates printed out in the loop.
#RequestMapping("/getListaRecept")
#ResponseBody
public ListaRecept sendAnswer(){
ListaRecept listaReceptFiltered = prescriptionCreator.createListaRecept();
for(Recepta r : listaReceptFiltered.getListaRecept()){
System.out.println(r.toString());
}
return listaReceptFiltered;
}
Dates are correct
Recepta{id=3, nazwa='nurofen', status=NOT_REALIZED, date=2017-07-27}
Recepta{id=1, nazwa='ibuprom', status=ANNULED, date=2014-12-25}
Recepta{id=2, nazwa='apap', status=REALIZED, date=2016-08-18}
And now I'm invoking this method from my SpringBoot app using restTemplate. And then received list is printed out
private final RestTemplate restTemplate;
public SgrService2(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
this.restTemplate.getMessageConverters()
.add(0, new StringHttpMessageConverter(Charset.forName("UTF-16")));
}
public ListaRecept getList() {
for(Recepta r : this.restTemplate.getForObject("http://localhost:8090/getListaRecept",
ListaRecept.class).getListaRecept()){
System.out.println(r.toString());
}
return this.restTemplate.getForObject("http://localhost:8090/getListaRecept",
ListaRecept.class);
}
As you can see all dates were replaced with current date :/
Recepta{id=3, nazwa='nurofen', status=NOT_REALIZED, date=2017-09-30}
Recepta{id=1, nazwa='ibuprom', status=ANNULED, date=2017-09-30}
Recepta{id=2, nazwa='apap', status=REALIZED, date=2017-09-30}
I have no idea what is going on...
Here you have pom dependencies
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
Thank you in advance for your help

It seems to me that you are using the wrong jackson module, instead of jsr310 (which I guess is for Java 8 date types), try using the artifact jackson-datatype-joda and register the module JodaModule.

Related

pjp.getArgs() is only capturing 1 argument

I am using ProceedingJoinPoint in my springboot application to capture arguments passed to log.info. I am using aspectjweaver-1.9.7. I have added the following dependencies in my maven pom.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
I have created a library which is basically a wrapper around slf4j logger. So whenever log.info or log.debug is called, I have written an #Aspect class called LoggerAspect , in which I have a #Around Method which will do the required work.
Here is the #Around Method
#Around("call(* org.slf4j.Logger.info(..))")
public void injectLogConfigInfo(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp.getArgs().length);
Object[] args = logMod(pjp.getArgs());
pjp.proceed(args);
MDC.clear();
}
Now, when i am implementing the logger in my application , i am facing the following issue:
Example: log.info("This is test of info", SomeObject);
when i am passing this, pjp is only picking up the first String but it is ignoring the object. I had read that it returns all the arguments as an object[] array. The method logmod(pjp.getArgs()) does some processing on the object array which pjp returns. I am not that proficient in posting questions so do forgive me for missing out on details.
Due to restrictions at my org, i cannot post the whole code but i have included the required bits. Now I know that when debugging, it is beneficial to view the whole picture but I do not have that priviledge. So even if you do not have the exact answer, if you can share your experience whether you have faced this issue and what you did to resolve it?
Thanks

Spring Boot, Sleuth, OTEL, and Honeycomb

I have a scenario where I have Spring Boot integrated with OTEL and shipping to Honeycomb.io. I am trying to add an environment tag to each trace. I have created a class:
#Component
public class EnvironmentSpanProcessor implements SpanProcessor {
#Value("${ENVIRONMENT")
private String environment;
Queue<SpanData> spans = new LinkedBlockingQueue<>(50);
#Override
public void onStart(Context context, ReadWriteSpan readWriteSpan) {
readWriteSpan.setAttribute("env", environment);
}
#Override
public boolean isStartRequired() {
return false;
}
#Override
public void onEnd(ReadableSpan readableSpan) {
this.spans.add(readableSpan.toSpanData());
}
#Override
public boolean isEndRequired() {
return true;
}
}
I have set break points in this class, and they never hit on startup, even though the bean can be seen in actuator. I have put breakpoints on:
SdkTracerProvider otelTracerProvider(SpanLimits spanLimits, ObjectProvider<List<SpanProcessor>> spanProcessors,
SpanExporterCustomizer spanExporterCustomizer, ObjectProvider<List<SpanExporter>> spanExporters,
Sampler sampler, Resource resource, SpanProcessorProvider spanProcessorProvider) {
SdkTracerProviderBuilder sdkTracerProviderBuilder = SdkTracerProvider.builder().setResource(resource)
.setSampler(sampler).setSpanLimits(spanLimits);
List<SpanProcessor> processors = spanProcessors.getIfAvailable(ArrayList::new);
processors.addAll(spanExporters.getIfAvailable(ArrayList::new).stream()
.map(e -> spanProcessorProvider.toSpanProcessor(spanExporterCustomizer.customize(e)))
.collect(Collectors.toList()));
processors.forEach(sdkTracerProviderBuilder::addSpanProcessor);
return sdkTracerProviderBuilder.build();
}
in OtelAutoConfiguration and am not seeing them firing either on startup.
My pom.xml relevant section is:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-brave</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-otel-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.47.0</version>
</dependency>
And my configuration from application.yaml
sleuth:
enabled: true
web:
additional-skip-pattern: /readiness|/liveness
client.skip-pattern: /readiness
sampler:
probability: 1.0
rate: 100
propagation:
type: OT_TRACER
otel:
config:
trace-id-ratio-based: 1.0
log.exporter.enabled: true
exporter:
otlp:
endpoint: https://api.honeycomb.io
headers:
x-honeycomb-team: ${TELEMETRY_API_KEY}
x-honeycomb-dataset: app-telemetry
sleuth-span-filter:
enabled: true
resource:
enabled: true
I am getting traces, so it appears the system itself is working, however I cannot get my env tag added.
Preemptive thank you to #marcingrzejszczak for the help so far on my gist: https://gist.github.com/fpmoles/b880ccfdef2d2138169ed398e87ec396
I'm unsure why your span processor is not being picked up by Spring and being added to your list of processors being registered with the tracer provider.
An alternative way to set process consistent values, like environment, would be to set it as a resource attribute. This is more desireable because it's set once and delivered once per batch of spans sent to the configured backend (eg Honeycomb). Using a span processor adds the same attribute to every span.
This can be done in a few different ways:
If using AutoConfigure, you can set via system property or environment variable
Set directly on the resource during your otelTracerProvider method:
resource.setAttribute("environment", "${environment}");
FYI Honeycomb has OTel Java SDK & Agent distros to help simplify sending data that reduces required configuration and sets sensible defaults.

Argument passed to when() is not a mock! exception thrown with Spring Boot project which doesn't have #SpringBootApplication/main class

My project is a simple spring boot application which doesn't have a main/#SpringBootApplication class. It is used as a dependency library for other modules. I am trying to write the unit tests for the classes present in this project like below and getting the below pasted error. Any quick help is much appreciated.
pom dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- exclude junit 4 -->
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
As this project doesn't have main class, to get the spring application context using below configuration class.
#Configuration
public class TestServiceConfig {
#Bean
public TestService productService() {
return Mockito.mock(TestService.class);
}
#Bean
public MongoDriverService productMongo() {
return Mockito.mock(MongoDriverService.class);
}
}
Below is my test class which is throwing exception. Actual java class has a method called getPlanCode(which takes 6 arguments) and returns void. In this method mongo object is used for connecting the db so that I used #InjectMocks on service object.
public class ValidationServiceTest {
#Mock
MongoDriverService mongo;
#InjectMocks
TestService service;
#Test
#DisplayName("Test Get Plan Code positive")
public void getPlanCodeTest() {
doNothing().when(service).getPlanCode(anyString(), anyString(), any(Batch.class), any(BatchFile.class), any(Document.class), anyString());
service.getPlanCode(anyString(), anyString(), any(Batch.class), any(BatchFile.class), any(Document.class), anyString());
verify(service, times(1)).getPlanCode(anyString(), anyString(), any(Batch.class), any(BatchFile.class), any(Document.class), anyString());
}
}
Below is the exception
12:51:33.829 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext#45b4c3a9 testClass = DefaultMedicareBFTAccumsValidationServiceTest, testInstance = com.anthem.rxsmart.service.standalone.batchvalidation.DefaultMedicareBFTAccumsValidationServiceTest#14dda234, testMethod = getPlanCodeTest#DValidationServiceTest, testException = org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!
Example of correct stubbing:
service is not a mock since you are using #InjectMocks ( assume you are using #RunWith(MockitoRunner.class) or #ExtendWith but you are hiding that for whatever reasons).
What #InjectMocks does, is create of a new instance of TestService and literally inject mocks into it (mocked required dependencies). So service is a real thing, not a mock
IMO this test makes not sense as you are suppose to test your implementation of singular entity contract, not to test mocks...
Your test case and assertions are pointless as it is like "call method A and check if I just called method A" while you should check and validate eg return value of a call, or if some methods of mocks have been called eg if Mongo was queried with proper arguments. I just hope it is a really bad example, not real test scenario
Also test setup is wrong as you show us that you want to use #Configuration class with #Bean but then you are using #Mock in the test which will create brand new mocks for you. In other words - that config is not used at all
Posting this answer just for the developers who are in same understanding state.
#Test
#DisplayName("Test Get Plan Code positive")
public void getPlanCodeTest() {
service = new ValidationService(mongo);
Mockito.when(mongo.aggregateIterable("test", pipeline)).thenReturn(tierFilterDocs);
service.getPlanCode("", "", null, batchFile, null, "");
verify(mongo, times(1)).aggregateIterable("test", pipeline);
}
I have updated my test case so it solves the purpose now. Now no need of the Configuration file as I am mocking the object in test class itself.

Avoid sending zero values to InfluxDB

I'm using Spring Boot Actuator + Micrometer to send values to micrometer, so i have the following maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-influx</artifactId>
</dependency>
So, i have a method that start timer and stop timer sample (micrometer):
public Timer.Sample starTimer() {
return Timer.start(registry);
}
public void stopTimer(Class clazz, Timer.Sample sample) {
sample.stop(registry.timer("timer-dev", Arrays.asList(Tag.of("modulo", modulo), Tag.of("class", clazz.getName()))));
}
So, this works very well. InfluxDB receive value sent from Spring Boot Application and show in Grafana.
The problem: After sending "timer-dev" first time to influx, spring boot keeping sending values "0" continually, i would like avoid spring boot sending zero values, it should send only when timer-dev greater then zero. The "timer-dev" should be sent only when this method is called not everytime.
Any tips ?

Testing with spring-test-mvc jsonpath returns null

I am using Spring's "spring-test-mvc" library to test web controllers. I have a very simple controller that returns a JSON array. Then in my test I have:
#Test
public void shouldGetAllUsersAsJson() throws Exception {
mockMvc.perform(get("/v1/users").accept(MediaType.APPLICATION_JSON))
.andExpect(content().mimeType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("fName").exists());
}
The above test returns:
java.lang.AssertionError: No value for JSON path: fName
To quickly check what I actually get I ran the below test:
#Test
public void shouldPrintResults() throws Exception {
mockMvc.perform(get("/v1/users").accept(MediaType.APPLICATION_JSON))
.andDo(print());
}
And it returns the correct JSON array in the body of MockHttpServletResponse
I'm not sure why jsonPath is not able to see fName in the JSON array.
If you add the json path dependency to maven, or add the jar to your lib, then it will work. I think that Spring is not including the jsonPath dependency in the latest Spring 3.2.0 RC1 release. I'm guessing that this is the same for Spring-Test-MVC standalone project as well.
Here is the dependency for Maven:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
<scope>test</scope>
</dependency>
You might also need the hamcrest library to use the jsonPath("$.test").value("test")
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
What does your json response body look like? You can see it by doing an .andDo(print())
You might want to try jsonPath("$.fName").
This is assuming that your json response is:
{"fName":"first name"}
If your response is an array then you need jsonPath("$[0].fName") for a response like:
[{"fName":"first name"},{"fName":"first name #2"}]
You can see more examples at: http://goessner.net/articles/JsonPath/

Resources