How Spring Hystrix helps when one microservice data requires in other microservices? - spring

Hystrix helps for false tolerence and latency tolerence.By using fallback method we can show empty data or some message that that service is down, but if MicroService1 id needs in other business logic how to handle this?
MicroService1-Here is my Sample Code
This is the API which will be called by Other microservice2. Its Needs to return one String as ID.
#RequestMapping(value = "/list")
public String list() {
return "Some Id";
}
Microservice2- Calling MicroService1 API
#HystrixCommand(commandProperties = { #HystrixProperty(name = "execution.timeout.enabled", value = "true"),
#HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
#HystrixProperty(name = "fallback.enabled", value = "true")
}, fallbackMethod = "fallback", commandKey = "list", groupKey = "EmployeeServiceImpl")
public String list() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(microservice1apiurl, String.class);
}
public String fallback() {
return "";
}

Related

set call limitation for some service

i am working on a spring boot project and i want ti set limitation for some service to call.for example i want in 1 minute,5 request send to this endpoint.
#GetMapping(path = "/allJobsByChartCode/{chartCode}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Page<?>> getAllOCMJobsByChartCode(#PathVariable("chartCode") String chartCode, #RequestParam int page, #RequestParam int size, #RequestParam(required = false, name = "orderby") String orderby, #RequestParam(required = false, name = "direction") String direction) {
Page<OCMJobsDTO> gridData = ocmJobsService.getAllOCmJobsByChartCode(chartCode, PageRequest.of(page, size), orderby, direction);
if (gridData.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(gridData, HttpStatus.OK);
}

ServiceResponse mocked which gives a null value and not expected this null

I'm writing j-unit Test-cases for my services and in which i couldn't mock service Response properly, Which is giving me a null. can somebody help me in this issue.
public ResponseEntity<Void> lockGet(
#ApiParam(value = "Unique identifier for this request.", required = true) #RequestHeader(value = "service-id", required = true) String serviceId,
#ApiParam(value = "Logged in userid.", required = true) #RequestHeader(value = "user-id", required = true) String userId,
#ApiParam(value = "Unique messageid.", required = true) #RequestHeader(value = "message-id", required = true) String messageId,
#RequestHeader(value = "access-token", required = true) String accessToken,
#ApiParam(value = "Unique id of the doamin of the entity", required = true) #RequestParam(value = "lockDomainId", required = true) Long lockDomainId,
#ApiParam(value = "Unique id of the entity to be fetched", required = true) #RequestParam(value = "lockEntityId", required = true) Long lockEntityId,
HttpServletRequest request, HttpServletResponse response) {
ResponseEntity<Void> result = null;
if (request.getAttribute("user-id") != null)
userId = (String) request.getAttribute("user-id");
String logContext = "||" + lockDomainId + "|" + lockEntityId + "||";
ThreadContext.put("context", logContext);
long t1 = System.currentTimeMillis();
LOG.info("Method Entry: lockGet" + logContext);
ServiceRequest serviceRequest = AppUtils.mapGetRequestHeaderToServiceRequest(serviceId, userId, lockDomainId,
lockEntityId);
try {
ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
// set all the response headers got from serviceResponse
HeaderUtils.setResponseHeaders(serviceResponse.getResponseHeaders(), response);
result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
} catch (Exception ex) {
LOG.error("Error in lockGet", ex);
result = new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
ThreadContext.put("responseTime", String.valueOf(System.currentTimeMillis() - t1));
LOG.info("Method Exit: lockGet");
return result;
}
#Test
public void testLockGetForError() {
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
LockService service = Mockito.mock(LockService.class);
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
I tried in different scenario's which couldn't fix this issue. Can someone help me out. Thanks in advance.
From the code that you have put , the issue that i see is that you are actually mocking the LockService object but when calling the lockApiController.lockGet method the code is not actually working with the mocked LockService since lockApiController has an LockService object of it's own.
One way to solve this issue is to inject the mocked LockService
object into the lockApiController object using #Spy. This way
when the getLock() is called it will be actually called on the
mocked object and will return the mock response provided.
So in your test :
#Test
public void testLockGetForError() {
LockService service = Mockito.mock(LockService.class);
LockApiController lockApiController = Mockito.spy(new LockApiController(service));
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
So you can try passing the mocked LockService object to the spy object.
Another way is to try using the #InjectMocks to inject the mocked
object into the LockApiController.
#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
For example:
#Mock
Map<String, String> wordMap;
#InjectMocks
MyDictionary dic = new MyDictionary();
#Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
For the class:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
For both of these to work , you must be having a constructor or appropriate setters to set the mock object to the LockApiController class.
Reference : https://howtodoinjava.com/mockito/mockito-annotations/

BackPressure in Spring Boot application

I want to apply backpressure on my application webservice. So that if number of request is high say more than N I can notify the client. Below is my controller
Before WebFlux
#RequestMapping(value = "/tasks", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseTaskModel> taskForUser(#RequestParam(value = "userId", required = true) String userId,
#RequestParam(required = false) Map<String, String> userData) {
ResponseTaskModel responseTaskModel = service.retrieveNextTaskForUser(userId.toLowerCase(), userData);
return new ResponseEntity<ResponseTaskModel>(responseTaskModel, HttpStatus.OK);
}
After WebFlux
#RequestMapping(value = "/tasks/v1", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono taskForUserV1(#RequestParam(value = "userId", required = true) String userId,
#RequestParam(required = false) Map<String, String> userData) {
return service.taskForUserV1(userId, userData);
}
And the service method I am calling is a blocking call to db with three queries, I'm using a postgres database.
Now how shall I apply backpressure on the Mono object returned?

Java: GroupSequenceProvider for Validation, object is null in getValidationGroups method

This is what I am trying to achieve:
I have an update request object and user is allowed to do Partial Updates. But I want to validate the field only if it is in the request body. Otherwise, it is OK to be null. To achieve this, I am using GroupSequenceProvider to let the Validator know what groups to validate. What am I doing wrong here? If there is a blunder, how do I fix it?
Documentation: https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html#example-implementing-using-default-group-sequence-provider
#GroupSequenceProvider(UpdateUserRegistrationGroupSequenceProvider.class)
public class UpdateUserRegistrationRequestV1 {
#NotBlank(groups = {EmailExistsInRequest.class})
#Email(groups = {EmailExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {EmailExistsInRequest.class})
private String email;
#NotNull(groups = {PasswordExistsInRequest.class})
#Size(min = 8, max = 255, groups = {PasswordExistsInRequest.class})
private String password;
#NotNull(groups = {FirstNameExistsInRequest.class})
#Size(max = 255, groups = {FirstNameExistsInRequest.class})
#SafeHtml(whitelistType = SafeHtml.WhiteListType.NONE, groups = {FirstNameExistsInRequest.class})
private String firstName;
// THERE ARE GETTERS AND SETTERS BELOW
}
Group Sequence Provider Code:
public class UpdateUserRegistrationGroupSequenceProvider implements DefaultGroupSequenceProvider<UpdateUserRegistrationRequestV1> {
public interface EmailExistsInRequest {}
public interface PasswordExistsInRequest {}
public interface FirstNameExistsInRequest {}
#Override
public List<Class<?>> getValidationGroups(UpdateUserRegistrationRequestV1 updateUserRegistrationRequestV1) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add(Default.class);
defaultGroupSequence.add(UpdateUserRegistrationRequestV1.class);
if(StringUtils.hasText(updateUserRegistrationRequestV1.getEmail())) {
defaultGroupSequence.add(EmailExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getPassword())) {
defaultGroupSequence.add(PasswordExistsInRequest.class);
}
if(StringUtils.hasText(updateUserRegistrationRequestV1.getFirstName())) {
defaultGroupSequence.add(FirstNameExistsInRequest.class);
}
return defaultGroupSequence;
}
}
I am using Spring MVC, so this is how my controller method looks,
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateUser(#PathVariable("userId") Long userId,
#RequestBody #Valid UpdateUserRegistrationRequestV1 request) {
logger.info("Received update request = " + request + " for userId = " + userId);
registrationService.updateUser(userId, conversionService.convert(request, User.class));
}
Now the problem is, the parameter "updateUserRegistrationRequestV1" in the UpdateUserRegistrationGroupSequenceProvider.getValidationGroups method is null. This is the request object that I am sending in the request body and I am sending email field with it.
What am I doing wrong?
I too went through the same issue ,and hopefully solved it
You just have to check the object is null and put all your conditions inside it.
public List<Class<?>> getValidationGroups(Employee object) {
List<Class<?>> sequence = new ArrayList<>();
//first check if the object is null
if(object != null ){
if (!object.isDraft()) {
sequence.add(Second.class);
}
}
// Apply all validation rules from default group
sequence.add(Employee.class);
return sequence;
}

Resource not invoked after Jersery Request Filter

I have a two SpringServlets defined, one of which directs to a custom filter
#WebServlet(urlPatterns = { "/" }, initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseyServlet extends SpringServlet
{
}
#WebServlet(name = "secure", urlPatterns = "/secure/*", initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.spi.container.ContainerRequestFilters", value = "com.x.y.resource.OAuthFilter"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseySecureServlet extends SpringServlet
{
}
The idea being that any URLs that contain "/secure/" are directed to the OAuthFilter to validate the request by the OAuth headers. All other URLs are just handled normally.
For any request sent to a URL not containing "/secure/", the appropriate JAX-RS resource class is invoked correctly. However, for URLs containing "/secure/" the OAuthFilter is invoked correctly, but the JAX-RS annotated Resource class is never invoked afterwards.
Here is an example secure resource
#Component
#Scope("request")
#Path("/secure/example")
public class SecureResource
{
#GET
...
}
and here is the OAuthFilter
#Provider
#Component
public class OAuthFilter implements ContainerRequestFilter
{
public static final String AUTHORIZED_USER = "authorized_user";
#Autowired
AccessTokenService accessTokenService;
#Autowired
UserService userService;
#Context
HttpServletRequest httpRequest;
#Override
public ContainerRequest filter(ContainerRequest containerRequest)
{
OAuthServerRequest request = new OAuthServerRequest(containerRequest);
OAuthParameters params = new OAuthParameters();
params.readRequest(request);
String accessToken = params.getToken();
if (accessToken == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
String userId = accessTokenService.getUserIdForToken(accessToken);
if (userId == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
User user = userService.get(userId);
if (user == null)
{
throw new WebApplicationException(Status.NOT_FOUND);
}
httpRequest.setAttribute(AUTHORIZED_USER, user);
return containerRequest;
}
}
It looks like once the JerseySecureServlet with the "/secure/*" mapping is selected and the OAuthFilter is invoked the baseURI is "http:/ip:port/context/secure" and the path is simply "/example", and no Resource corresponds to this path, so nothing is invoked. What should I be doing instead to only apply this filter to URLs that contain "/secure/"?
I have solved the problem, but I am not 100% sure if my solution is the correct way to do it. I have changed the annotation on the filtered serlvet to be #WebFilter, giving me
#WebServlet(urlPatterns = { "/*" }, initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseyServlet extends SpringServlet
{
}
#WebFilter(urlPatterns = "/secure/*", initParams = {
#WebInitParam(name = "com.sun.jersey.config.property.packages", value = "com.x.y.resource"),
#WebInitParam(name = "com.sun.jersey.spi.container.ContainerRequestFilters", value = "com.x.y.resource.OAuthFilter"),
#WebInitParam(name = "com.sun.jersey.api.json.POJOMappingFeature", value = "true") })
public class JerseySecureFilter extends SpringServlet
{
}
and this works. A better solution will be accepted over this one.

Resources