diff --git a/configuration-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/configuration/ConfigurationServiceApplication.java b/configuration-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/configuration/ConfigurationServiceApplication.java
index 7e49fa8..3c96dbd 100644
--- a/configuration-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/configuration/ConfigurationServiceApplication.java
+++ b/configuration-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/configuration/ConfigurationServiceApplication.java
@@ -9,15 +9,16 @@
@EnableConfigServer
public class ConfigurationServiceApplication {
+ private static String FP;
+
@Value("${spring.cloud.config.server.native.search-locations}")
- public void setBaseLocation(String baseLocation) {
- BL = baseLocation;
+ public void setFP(String filePath) {
+ FP = filePath;
}
- static String BL;
public static void main(String[] args) {
SpringApplication.run(ConfigurationServiceApplication.class, args);
- System.out.println(BL);
+ System.out.println(FP);
}
}
diff --git a/configuration-service/src/main/resources/bootstrap.properties b/configuration-service/src/main/resources/bootstrap.properties
index ac24950..e7b6539 100644
--- a/configuration-service/src/main/resources/bootstrap.properties
+++ b/configuration-service/src/main/resources/bootstrap.properties
@@ -6,4 +6,6 @@ server.port=8888
eureka.instance.prefer-ip-address=true
-spring.cloud.loadbalancer.ribbon.enabled=false
\ No newline at end of file
+spring.cloud.loadbalancer.ribbon.enabled=false
+
+spring.cloud.config.server.native.search-locations=file:${PROJECT_BASE_DIR}/data/configuration-store
\ No newline at end of file
diff --git a/data/configuration-store/polling-service-development.properties b/data/configuration-store/polling-service-development.properties
index 55786ca..a3d0a7a 100644
--- a/data/configuration-store/polling-service-development.properties
+++ b/data/configuration-store/polling-service-development.properties
@@ -7,12 +7,12 @@ spring.redis.port=6379
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
ps.refresh.search.location=Stuttgart
-ps.refresh.cron.expression=*/60 * * * * *
-ps.cleanup.cron.expression=*/120 * * * * *
-ps.cleanup.staleness.interval=30
-ps.cleanup.ttl.eviction=30
+ps.refresh.cron.expression=*/20 * * * * *
+ps.cleanup.cron.expression=*/60 * * * * *
+ps.cleanup.staleness.interval=10
+ps.cleanup.ttl.eviction=10
-car2go.base.url=http://192.168.0.109:3000
+car2go.base.url=http://192.168.0.111:3000
car2go.vehicles.by.location.uri=/vehicles/{locationName}
#spring.redis.redisson.file=classpath:redisson.yaml
diff --git a/data/configuration-store/position-service-development.properties b/data/configuration-store/position-service-development.properties
index ea6fa4a..ede5a99 100644
--- a/data/configuration-store/position-service-development.properties
+++ b/data/configuration-store/position-service-development.properties
@@ -14,4 +14,9 @@ poss.car-service.url=localhost:17080/car
poss.placement.infinite.end.limit=1000
-eureka.instance.prefer-ip-address=true
\ No newline at end of file
+eureka.instance.prefer-ip-address=true
+
+poss.circuit-breaker.failure.threshold-percentage=30
+poss.circuit-breaker.wait.duration.in-open-state=3000
+poss.circuit-breaker.sliding-window.size=2
+poss.circuit-breaker.timeout.duration=10
\ No newline at end of file
diff --git a/data/configuration-store/position-service-staging.properties b/data/configuration-store/position-service-staging.properties
index 2c6b5c8..d11bb1c 100644
--- a/data/configuration-store/position-service-staging.properties
+++ b/data/configuration-store/position-service-staging.properties
@@ -7,4 +7,9 @@ spring.sleuth.web.skipPattern=(^cleanup.*|.+favicon.*)
poss.placement.infinite.end.limit=1000
-eureka.instance.prefer-ip-address=true
\ No newline at end of file
+eureka.instance.prefer-ip-address=true
+
+poss.circuit-breaker.failure.threshold-percentage=30
+poss.circuit-breaker.wait.duration.in-open-state=3000
+poss.circuit-breaker.sliding-window.size=3
+poss.circuit-breaker.timeout.duration=3
\ No newline at end of file
diff --git a/position-service/pom.xml b/position-service/pom.xml
index 3d324da..6a6a762 100644
--- a/position-service/pom.xml
+++ b/position-service/pom.xml
@@ -61,6 +61,10 @@
jts-core
${jts.version}
+
+ org.springframework.cloud
+ spring-cloud-starter-circuitbreaker-resilience4j
+
diff --git a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/configuration/PositionServiceConfiguration.java b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/configuration/PositionServiceConfiguration.java
index 858c769..9034b46 100644
--- a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/configuration/PositionServiceConfiguration.java
+++ b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/configuration/PositionServiceConfiguration.java
@@ -1,6 +1,12 @@
package com.teenthofabud.codingchallenge.sharenow.position.configuration;
+import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
+import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.locationtech.jts.geom.GeometryFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
+import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
+import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
@@ -9,6 +15,7 @@
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
+import java.time.Duration;
import java.util.Locale;
@Component
@@ -36,4 +43,25 @@ public GeometryFactory geometryFactory() {
}
+ @Bean
+ public Customizer globalCircuitBreakerFactory(
+ @Value("${poss.circuit-breaker.failure.threshold-percentage}") float failureThresholdPercentage,
+ @Value("${poss.circuit-breaker.wait.duration.in-open-state}") long waitDurationInOpenStateInMillis,
+ @Value("${poss.circuit-breaker.sliding-window.size}") int slidingWindowSize,
+ @Value("${poss.circuit-breaker.timeout.duration}") long timeoutDurationInSeconds) {
+
+ CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
+ .failureRateThreshold(failureThresholdPercentage)
+ .waitDurationInOpenState(Duration.ofMillis(waitDurationInOpenStateInMillis))
+ .slidingWindowSize(slidingWindowSize)
+ .build();
+ TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
+ .timeoutDuration(Duration.ofSeconds(timeoutDurationInSeconds))
+ .build();
+ return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
+ .timeLimiterConfig(timeLimiterConfig)
+ .circuitBreakerConfig(circuitBreakerConfig)
+ .build());
+ }
+
}
diff --git a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PlacementServiceJTSImpl.java b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PlacementServiceJTSImpl.java
index 1a3d722..7c573ff 100644
--- a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PlacementServiceJTSImpl.java
+++ b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PlacementServiceJTSImpl.java
@@ -36,14 +36,18 @@ public boolean isCarInsidePolygon(CarDetailsDTO carDTO, StrategicPolygonDetailed
if (coordinates != null && coordinates.size() < 3) {
throw new PositionServiceException("Invalid polygon", PositionErrorCode.UNEXPECTED_PARAMETER, new Object[] {"polygon vertex count < 3"});
} else {
- List polygonPointsList = polygonDTO.getGeometry().getExteriorRing().stream()
- .map(lla -> new Coordinate(lla.getLongitude(), lla.getLatitude())).collect(Collectors.toList());
- polygonPointsList.add(polygonPointsList.get(0)); // sanitizing polygon coordinates as per GeoJSOn specifications
- Coordinate[] polygonPointsArray = polygonPointsList.toArray(new Coordinate[polygonPointsList.size()]);
- Polygon strategicPolygon = geometryFactory.createPolygon(polygonPointsArray);
- Coordinate carCoordinates = new Coordinate(carDTO.getPosition().getLongitude(), carDTO.getPosition().getLatitude());
- Point carLocation = geometryFactory.createPoint(carCoordinates);
- return strategicPolygon.contains(carLocation) || strategicPolygon.getBoundary().contains(carLocation);
+ if(carDTO.getPosition() != null) {
+ Coordinate carCoordinates = new Coordinate(carDTO.getPosition().getLongitude(), carDTO.getPosition().getLatitude());
+ List polygonPointsList = polygonDTO.getGeometry().getExteriorRing().stream()
+ .map(lla -> new Coordinate(lla.getLongitude(), lla.getLatitude())).collect(Collectors.toList());
+ polygonPointsList.add(polygonPointsList.get(0)); // sanitizing polygon coordinates as per GeoJSOn specifications
+ Coordinate[] polygonPointsArray = polygonPointsList.toArray(new Coordinate[polygonPointsList.size()]);
+ Polygon strategicPolygon = geometryFactory.createPolygon(polygonPointsArray);
+ Point carLocation = geometryFactory.createPoint(carCoordinates);
+ return strategicPolygon.contains(carLocation) || strategicPolygon.getBoundary().contains(carLocation);
+ } else {
+ throw new PositionServiceException("Invalid car position", PositionErrorCode.UNEXPECTED_PARAMETER, new Object[] {"car position is not provided"});
+ }
}
} else {
throw new PositionServiceException("Invalid polygon", PositionErrorCode.INVALID_PARAMETER, new Object[] {"polygon coordinates"});
diff --git a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PositionServiceImpl.java b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PositionServiceImpl.java
index 1470eb0..a106fc5 100644
--- a/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PositionServiceImpl.java
+++ b/position-service/src/main/java/com/teenthofabud/codingchallenge/sharenow/position/service/impl/PositionServiceImpl.java
@@ -1,6 +1,5 @@
package com.teenthofabud.codingchallenge.sharenow.position.service.impl;
-import com.ctc.wstx.sw.EncodingXmlWriter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.teenthofabud.codingchallenge.sharenow.position.model.dto.car.CarDetailsDTO;
@@ -9,11 +8,13 @@
import com.teenthofabud.codingchallenge.sharenow.position.model.dto.polygon.StrategicPolygonDetailedDTO;
import com.teenthofabud.codingchallenge.sharenow.position.model.error.PositionErrorCode;
import com.teenthofabud.codingchallenge.sharenow.position.model.error.PositionServiceException;
-import com.teenthofabud.codingchallenge.sharenow.position.model.vo.*;
+import com.teenthofabud.codingchallenge.sharenow.position.model.vo.ErrorVO;
import com.teenthofabud.codingchallenge.sharenow.position.model.vo.car.Car2StrategicPolygonPositioningVO;
import com.teenthofabud.codingchallenge.sharenow.position.model.vo.car.CarMappedVO;
import com.teenthofabud.codingchallenge.sharenow.position.model.vo.car.PositionVO;
-import com.teenthofabud.codingchallenge.sharenow.position.model.vo.polygon.*;
+import com.teenthofabud.codingchallenge.sharenow.position.model.vo.polygon.GeoFeatureVO;
+import com.teenthofabud.codingchallenge.sharenow.position.model.vo.polygon.StrategicPolygon2CarPositioningVO;
+import com.teenthofabud.codingchallenge.sharenow.position.model.vo.polygon.StrategicPolygonMappedVO;
import com.teenthofabud.codingchallenge.sharenow.position.repository.CarServiceClient;
import com.teenthofabud.codingchallenge.sharenow.position.repository.PolygonServiceClient;
import com.teenthofabud.codingchallenge.sharenow.position.service.PlacementService;
@@ -22,6 +23,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
+import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@@ -45,6 +48,13 @@ public class PositionServiceImpl implements PositionService {
@Autowired
private PolygonServiceClient polygonClient;
+ @Autowired
+ private CircuitBreakerFactory globalCircuitBreakerFactory;
+
+ private CircuitBreaker carServiceCircuitBreaker;
+
+ private CircuitBreaker polygonServiceCircuitBreaker;
+
private Converter carDetailsDTO2VOConverter;
private Converter polygonDetailsDTO2VOConverter;
@@ -55,6 +65,8 @@ public class PositionServiceImpl implements PositionService {
@PostConstruct
public void init() {
+ this.carServiceCircuitBreaker = globalCircuitBreakerFactory.create("car");
+ this.polygonServiceCircuitBreaker = globalCircuitBreakerFactory.create("polygon");
this.positionDTO2VOConverter = (dto) -> {
PositionVO vo = new PositionVO();
vo.setLatitude(dto.getLatitude());
@@ -108,14 +120,42 @@ private PositionServiceException parseFeignErrorResponse(FeignException e) {
return new PositionServiceException(e.getMessage(), PositionErrorCode.SYSTEM_ERROR, new Object[] {response});
}
+ private CarDetailsDTO getDefaultCarDetailsByVin(String vin) {
+ CarDetailsDTO carDetailsDTO = new CarDetailsDTO();
+ carDetailsDTO.setVin(vin);
+ return null;
+ }
+
+ private List getDefaultCarDetailsList() {
+ return new ArrayList();
+ }
+
+ private List getDefaultStrategicPolygons() {
+ return new ArrayList();
+ }
+
+ private StrategicPolygonDetailedDTO getDefaultStrategicPolygonDetailsById(String polygonId) {
+ StrategicPolygonDetailedDTO dto = new StrategicPolygonDetailedDTO();
+ dto.setId(polygonId);
+ return dto;
+ }
+
+ private List getDefaultStrategicPolygonsAndTheirDetailsByName(String name) {
+ StrategicPolygonDetailedDTO dto = new StrategicPolygonDetailedDTO();
+ dto.setName(name);
+ return Arrays.asList(dto);
+ }
+
@Override
public Car2StrategicPolygonPositioningVO retrievePositionOfCarAndItsEnclosingPolygonByVin(String vin) throws PositionServiceException {
try {
Car2StrategicPolygonPositioningVO posVO = new Car2StrategicPolygonPositioningVO();
boolean found = false;
- CarDetailsDTO carDetailsDTO = this.carClient.getCarDetailsByVin(vin);
+ CarDetailsDTO carDetailsDTO = this.carServiceCircuitBreaker.run(() -> this.carClient.getCarDetailsByVin(vin),
+ throwable -> this.getDefaultCarDetailsByVin(vin));
LOGGER.info("Retrieved car details for vin: {}", vin);
- List polygonDTOList = this.polygonClient.getAllPolygons();
+ List polygonDTOList = this.polygonServiceCircuitBreaker.run(() -> this.polygonClient.getAllPolygons(),
+ throwable -> this.getDefaultStrategicPolygons());
if(polygonDTOList != null && !polygonDTOList.isEmpty()) {
LOGGER.info("Retrieved strategic polygons: {}", polygonDTOList.size());
for(StrategicPolygonDetailedDTO detailedPolygonDTO : polygonDTOList) {
@@ -147,9 +187,13 @@ public Car2StrategicPolygonPositioningVO retrievePositionOfCarAndItsEnclosingPol
public StrategicPolygon2CarPositioningVO retrievePositionsOfAllCarsWithinPolygonByPolygonId(String polygonId) throws PositionServiceException {
try {
StrategicPolygon2CarPositioningVO posVO = new StrategicPolygon2CarPositioningVO();
- StrategicPolygonDetailedDTO polygonDTO = this.polygonClient.getPolygonDetailsById(polygonId);
+ //StrategicPolygonDetailedDTO polygonDTO = this.polygonClient.getPolygonDetailsById(polygonId);
+ StrategicPolygonDetailedDTO polygonDTO = this.polygonServiceCircuitBreaker.run(() -> this.polygonClient.getPolygonDetailsById(polygonId),
+ throwable -> this.getDefaultStrategicPolygonDetailsById(polygonId));
LOGGER.info("Retrieved strategic polygon details for id: {}", polygonId);
- List carDetailsDTOList = this.carClient.getAllCarsWithDetails();
+ //List carDetailsDTOList = this.carClient.getAllCarsWithDetails();
+ List carDetailsDTOList = this.carServiceCircuitBreaker.run(() -> this.carClient.getAllCarsWithDetails(),
+ throwable -> this.getDefaultCarDetailsList());
if(carDetailsDTOList != null && !carDetailsDTOList.isEmpty()) {
LOGGER.info("Retrieved cars: {}", carDetailsDTOList.size());
for(CarDetailsDTO carDetailsDTO : carDetailsDTOList) {
@@ -179,9 +223,13 @@ public StrategicPolygon2CarPositioningVO retrievePositionsOfAllCarsWithinPolygon
public Set retrievePositionsOfAllCarsWithinPolygonByPolygonName(String name) throws PositionServiceException {
Set posVOList = new TreeSet<>();
try {
- List polygonDTOList = this.polygonClient.getAllPolygonsByName(name);
+ //List polygonDTOList = this.polygonClient.getAllPolygonsByName(name);
+ List polygonDTOList = this.polygonServiceCircuitBreaker.run(() -> this.polygonClient.getAllPolygonsByName(name),
+ throwable -> this.getDefaultStrategicPolygonsAndTheirDetailsByName(name));
LOGGER.info("Retrieved strategic polygon details for name: {}", name);
- List carDetailsDTOList = this.carClient.getAllCarsWithDetails();
+ //List carDetailsDTOList = this.carClient.getAllCarsWithDetails();
+ List carDetailsDTOList = this.carServiceCircuitBreaker.run(() -> this.carClient.getAllCarsWithDetails(),
+ throwable -> this.getDefaultCarDetailsList());
if((carDetailsDTOList != null && !carDetailsDTOList.isEmpty()) &&
(polygonDTOList != null && !polygonDTOList.isEmpty())){
LOGGER.info("Retrieved cars: {}", carDetailsDTOList.size());
@@ -190,7 +238,7 @@ public Set retrievePositionsOfAllCarsWithinPo
StrategicPolygon2CarPositioningVO posVO = new StrategicPolygon2CarPositioningVO();
List auxCarDetailsDTOList = new ArrayList<>();
for(CarDetailsDTO carDetailsDTO : carDetailsDTOList) {
- if(this.placementService.isCarInsidePolygon(carDetailsDTO, polygonDetailedDTO)) {
+ if (this.placementService.isCarInsidePolygon(carDetailsDTO, polygonDetailedDTO)) {
CarMappedVO carVO = this.carDetailsDTO2VOConverter.convert(carDetailsDTO);
posVO.addCar(carVO);
atleast1CarFound = true;