circular reference in my spring boot 3 application : The dependencies of some of the beans in the application context form a cycle

61 Views Asked by At

I have a circular reference issues in my Spring boot 3 application on one of my service and a Spring boot configuration class and I have no idea to fix it.

I doing a Spring boot migration from version 2 to version 3, and we have a MonitoringService which inject another service but in the another service we dont have any injection. So in fact we should'nt have circular reference between these 2 services.

┌─────┐
|  monitoringService defined in URL [jar:file:/C:/dev/sources/project/target/myproject-0.3.0-SNAPSHOT.jar!/BOOT-INF/classes!/com/omb/project/services/MonitoringService.class]
↑    ↓
|  healthContributorRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]
└─────┘

MonitoringService

package com.omb.services;

import com.omb.restcore.services.HealthCheckSimpleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.health.*;
import org.springframework.stereotype.Component;

@Component
@Endpoint(id = "health")
public class MonitoringService extends HealthEndpoint implements HealthIndicator {

    @Autowired
    private HealthCheckSimpleService healthCheckSimpleService;

    public MonitoringService(HealthContributorRegistry registry, HealthEndpointGroups groups) {
        super(registry, groups, null);
    }

    @Override
    public Health getHealth(boolean includeDetails) {
        return HealthIndicator.super.getHealth(includeDetails);
    }

    @Override
    public Health health() {
        String status = healthCheckSimpleService.healthCheck(null, null).getBody();
        if ("RUNNING".equalsIgnoreCase(status)) {
            return new Health.Builder(Status.UP).build();
        }
        return new Health.Builder(Status.DOWN).build();
    }
}

HealthService

package com.omb.restcore.services;

import com.omb.restcore.exception.InternalServerException;
import com.omb.restcore.services.utils.HTTPHelper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.logging.Level;
import java.util.logging.Logger;

@CrossOrigin
@RestController
public class HealthCheckSimpleService {
    private static final Logger LOG = Logger.getLogger(HealthCheckSimpleService.class.getName());
    @Value("${api.healthMessage:API is healthy}")
    private String healthMessage;
    @Value("${api.maintenanceMessage:API is in maintenance}")
    private String maintenanceMessage;
    @Value("${apiStatus:RUNNING}")
    private Status status;

    private static void waitToDiscourageAttacks() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // Spend some time waiting to discourage DOS attacks
            throw new RuntimeException(e);
        }
    }

    @RequestMapping(value = "healthCheck", method = RequestMethod.GET)
    public ResponseEntity<String> healthCheck(HttpServletRequest request, HttpServletResponse response) {
        String message;

        if (status == Status.RUNNING) {
            message = healthMessage;
        } else if (status == Status.MAINTENANCE) {
            message = maintenanceMessage;
        } else {
            throw new InternalServerException("Unsupported status -> " + status);
        }

        waitToDiscourageAttacks();

        return new ResponseEntity<String>(message, HttpStatus.OK);
    }

    @RequestMapping(value = "status", method = RequestMethod.GET)
    public @ResponseBody
    Status[] getStatusList(HttpServletRequest request, HttpServletResponse response) {
        return Status.values();
    }

    @RequestMapping(value = "status", method = RequestMethod.PUT, consumes = MediaType.TEXT_PLAIN_VALUE)
    public @ResponseBody
    Status setStatus(HttpServletRequest request, HttpServletResponse response,
                     @RequestBody(required = true) String status) {
        this.status = Status.valueOf(status);

        LOG.log(Level.WARNING, "User " + HTTPHelper.getUserFromAuthentication() + " changed status to " + status
                + " from IP " + HTTPHelper.getRequesterIp(request));

        return this.status;
    }

    private enum Status {
        RUNNING, MAINTENANCE
    }
}
1

There are 1 best solutions below

0
Ousmane MBINTE On

I fix my issue with the code below

package com.omb.gateway.services;

import com.omb.restcore.services.HealthCheckSimpleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;

@Component
public class MonitoringService implements HealthIndicator {

    @Autowired
    private HealthCheckSimpleService healthCheckSimpleService;


    @Override
    public Health health() {
        String status = healthCheckSimpleService.healthCheck(null, null).getBody();
        if ("RUNNING".equalsIgnoreCase(status)) {
            return new Health.Builder(Status.UP).build();
        }
        return new Health.Builder(Status.DOWN).build();
    }
}