I have 2 microservices, one of them producing APPLICATION_NDJSON data fetched from ReactiveMongoRepository
#RestController
#RequestMapping("/documents")
#RequiredArgsConstructor
public class DocumentController {
private final ReactiveMongoRepository<DocumentWrapper, ObjectId> repository;
#GetMapping(produces = MediaType.APPLICATION_NDJSON_VALUE)
public Flux<DocumentWrapper> getAll() {
return repository.findAll();
}
}
The another one consumes it using WebClient
public Flux<DocumentWrapper> getDocuments() {
return webClient.method(HttpMethod.GET)
.uri(properties.getUrl() + "/documents")
.contentType(MediaType.APPLICATION_NDJSON)
.retrieve()
.bodyToFlux(DocumentWrapper.class);
}
Recently, I discovered that sometimes the producing microservice stops streaming data after a while with no reason - no error or no cancel command is generated. Why does it happen and how could it be fixed?
I implemented a project demonstrating this behavior, you could find it on GitHub
P.S.
I guess the problem is in integration between reactive mongodb driver and spring rest controller (or netty). I replaced database access
repository.findAll()
with mock data like
Flux.generate(...)
and the problem was gone.
I also replaced data streaming from database to file not to controller and it worked fine too.
Related
I have a Java project with various #RequestMapping annotations.I want to make a new project which can use this #RequestMapping,is that possible
Of course you can.
If I understand your question correctly, do you want to use the data provided by a Spring application in another application?
It's not that hard, you just have to keep a few things in mind.
The applications have to run on different ports, of course both applications have to be started.
For example, App1 has a #RequestMapping #GetMapping for personal data.
The path is e.g. http://localhost:8080/persondata
In the second application, you only need to address the API endpoint if you need this data.
This can be done with RestTemplate, for example.
#RestController
#RequestMapping("/persondata")
class PersonDataRestController {
private final Service personService;
public PersonDataRestController(Service personService) {
this.personService = personService;
}
#GetMapping
public ResponseEntity<Collection<?>> getAllPersonData() {
return ResponseEntity.ok(personService.allPersonData());
}
}
You just have to replace the Service with your PersonService or whatelse.
in the second application you can call the REST endpoint with RestTemplate for example
RestTemplate template = new RestTemplate();
try{
ResponseEntity<ArrayList<?>> response = template.exchange("http://localhost:8080/persondata", HttpMethod.GET, null, new ParameterizedTypeReference<ArrayList<?>>() {});
In terms of the specific application, you may need a DTO object.
For this topic i can suggest you this website
I hope I could answer your question
I have several applications monitored under Spring Boot Admin. Spring Boot Admin is great at telling me if an application is up or down and other various metrics.
I would also like to know that certain URLs exposed by these applications are returning an HTTP status of 200. Specifically, I would like to send a GET request to these URLs once a day. If it receives a non 200 status from any of these, it sends an email stating which URLs are reporting non 200.
Is something that Spring Boot Admin is able to do? I know about custom HealthIndicators but not sure if it can be scheduled or if it's appropriate for this.
Just wanted to see if there is something Spring Boot Admin offers to support doing this before I build my own app to make the GET calls and send the email.
Update
The URLs are exposed as Eureka services and I'm calling services from other services via Spring Cloud OpenFeign.
Update 2
I went ahead and built my own custom application to handle this. Details follow but still interested if Spring offers something out-of-the-box to do this.
application.yml
app:
serviceUrls:
- "http://car-service/cars?category=sedan"
- "http://truck-service/trucks"
cron: "0 0 10 * * *"
Urls are read into:
#Component
#ConfigurationProperties(prefix = "app")
#Getter
#Setter
public class ServiceUrls {
private String[] serviceUrls;
}
Via cron, scheduled to run once a day:
#Component
#RequiredArgsConstructor
#Slf4j
public class ServiceCheckRunner {
private final ServiceHealth serviceHealth;
#Scheduled(cron = "${cron}")
public void runCheck() {
serviceHealth.check();
}
}
This is the code that checks whether URLs return no errors:
#Service
#RequiredArgsConstructor
#Slf4j
public class ServiceHealth {
private final ServiceUrls serviceUrls;
private final RestTemplate rest;
public void check() {
List<String> failedServiceUrls = new ArrayList<>();
for (String serviceUrl : serviceUrls.getServiceUrls()) {
try {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
} catch (Exception e){
failedServiceUrls.add(serviceUrl);
}
}
// code to send an email with failedServiceUrls.
}
}
You can use Spring Boot Admin in order to send email notifications whenever a registered client changes his status from UP to OFFLINE or otherwise.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.4.0</version>
</dependency>
application.properties
spring.mail.host=smtp.example.com
spring.mail.username=smtp_user
spring.mail.password=smtp_password
spring.boot.admin.notify.mail.to=admin#example.com
But, if you really need to check client status once per day, you need to implement a custom solution.
In my Spring boot controller, I am having a method that inserts some records to the backend, at the end of this operation, I notify the user via Javax email based on the response received from from previous operation.
Currently I get response from API after email method completes.
Is there any way I can return response to the client once my first operation is completed while email notification happens in background
I tried already implement Async annotation in sendemail method of mail service. But I cannot find any difference in response time and I still get the response only after the email is sent.
My pseudo code
Controller:
#Autowired
private EmailService emailService;
#PostMapping(value = "create", produces = "text/plain")
private insertRecord()
{
response = <Insert into DB>;
sendEmail(response);
}
private sendEmail(response)
{
//check if email should be sent and if yes
emailservice.send(response);
}
Email service:
#Service
public class EmailService {
#Async
public static void sendEmail(MailEvent mailEvent) throws IOException {//send
email}
}
Starter
#SpringBootApplication
#EnableAsync
public class Starter {...}
i didn't notice my async method was designed as static, removing static works for me.
I think Spring Async is the way to go to solve your problem. Did you you also enable the async functionality with #EnableAsync and create an Executor bean? See this guide for the full tutorial: https://spring.io/guides/gs/async-method/
I know this is usually an issue that happens the other way around, so I am caught a little of guard here :D
I have built a user-management backend that provides a UI with data. When this architecture is deployed on our dev-server, everything works beautifully. However, as soon as I try to run the integration tests (which we do using a maven cargo tomcat) or if I use the war file in a local tomcat, the exception handlers aren't used at all. Spring simply displays a standard 500 response with the exception transformed into the body.
Perusing SO for similar issues has only resulted in the advice that I should use #EnableWebMVC, but that is neither applicable to what my backend is trying to accomplish, nor does it change anything.
How should I go about looking for the solution to this issue? Specifically, can I somehow observe if my controlleradvice is even scanned, and is there a reason why it might not be?
EDIT: These are the relevant files:
SpringConfiguration:
#Configuration
#ComponentScan(basePackageClasses = {UserManagementSpringConfiguration.class})
#EnableWebSecurity
public class UserManagementSpringConfiguration {
#Configuration
public static class ResourceMappingConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ui/*/usermanagement").setViewName("forward:/usermanagement-ui/index.html");
// registry.addViewController("/ui/*/*/generator/").setViewName("forward:/generator-ui/index.html");
registry.addViewController("/ui/*/usermanagement/*").setViewName("forward:/usermanagement-ui/index.html");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// cache setting, otherwise fonts are not loaded in IE over https
CacheControl cacheControl = CacheControl.noCache().mustRevalidate();
registry.addResourceHandler("/ui/**/*").addResourceLocations("/usermanagement-ui/")
.setCacheControl(cacheControl);
}
}
}
ControllerAdvice:
#ControllerAdvice
public class CustomResponseEntityExceptionHandler {
public static final Logger LOG = EISLoggerFactory.getLogger(CustomResponseEntityExceptionHandler.class);
#PostConstruct
public void postConstruct() {
LOG.debug("CustomExceptionHandler loaded and ready for use");
}
#ExceptionHandler(PasswordMismatchException.class)
public final ResponseEntity<ErrorDetails> handlePasswordChangeMismatch(
PasswordMismatchException ex,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(
new Date(),
ex.getMessage(),
request.getDescription(false),
MessageKeys.mismatchedPassword);
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
It turns out that one of the modules that we wrote and that my project contains defines an ExceptionHandler for Throwable.class. On my machine, this ControllerAdvice is registered before my own ControllerAdvice, which causes Spring to look there first. Since Throwable fits the bill, Spring asks no further questions and just uses that handler.
The solution to my immediate problem was to add #Order(Ordered.HIGHEST_PRECEDENCE) to my ControllerAdvice. Since the exceptions I define within are quite specific, this will not cause any issues.
I have yet to find an explanation for why the order in which the two ControllerAdvice classes are registered is so consistently different between my machine and our dev server. Will update if I find anything. For now, I consider this issue to be answered.
This SO question was essential to solving this particular problem. Perhaps it helps someone in the future to link it here: Setting Precedence of Multiple #ControllerAdvice #ExceptionHandlers
Thanks to ValentinCarnu for pointing me to it!
I have a Spring boot application that uses the actuator metrics and I have the following issue:
I have an endpoint like this :
GET /users/{userId}
So every time I call this endpoint I use a different Id to get the specific user as you can think we can have hundreds of thousands. It is working correct, but i noticed that when calling my metrics endpoint :
GET /metrics
I get something like this:
counter.status.200.metrics: 1,
counter.status.200.api.users.4: 2,
counter.status.200.api.users.2: 3,
counter.status.200.api.users.3: 2,
So it makes me think that i will get a counter for every single call with different path params, so my question is how can i have a counter just for the endpoint /users/{anyId} and not for every single combination excluding the parameters?.
--- EDIT ---
I'm using Spring boot + Jersey (I'm not using Spring MVC), the following is my controller code:
#Component
#Path("/users")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class UserResource {
#GET
public Response getUsers() {
return Response.ok("It works !").build();
}
#GET
#Path("/{userId}")
public Response getUserById(#PathParam("userId") String id) {
return Response.ok("It works !").build();
}
}
And the following is the Jersey configuration:
#Component
#ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(UserResource.class);
register(PingResource.class);
}
}
It's a known issue with Spring Boot 1.x when using Jersey. There's a workaround described in the issue, alternatively you can disable the metrics filter by adding the following to application.properties:
endpoints.metrics.filter.enabled=false