Test Fallback method using sping-cloud-starter-circuitbreaker-resilience4j - spring-boot

public String getAlbumList() {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
String url = "http://localhost:1234/not-real";
return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class),
throwable -> getDefaultAlbumList());
}
Assume this is my code and "getDefaultAlbumList()" is returning some string "ABCD", and let say my "restTemplate.getForObject(url, String.class)" is going to call some URL which may take more than 5 second, now point is
I want to test that fallback method, how do i test it, how do i mock so that my controller return fallback response.
Thanks

I suppose that you are trying out the example from here: Quick Guide to Spring Cloud Circuit Breaker. So, I copied their service and I introduced a method to pass a null URL. Then I created a test in a spring boot project that verifies if the string value that returns is the fallback string.
#Service
public class AlbumService {
private RestTemplate restTemplate = new RestTemplate();
#Autowired
private CircuitBreakerFactory circuitBreakerFactory;
public String getAlbumList() {
return getAlbumList("https://jsonplaceholder.typicode.com/albums");
}
public String getAlbumList(String url) {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class),
throwable -> getDefaultAlbumList());
}
// fallback method that reads a default json file
private String getDefaultAlbumList() {
try {
return new String(Files.readAllBytes(
Paths.get(getClass().getClassLoader().getResource("data/fallback-album-list.json").toURI())
));
} catch (Exception e) {
LOGGER.error("error occurred while reading the file", e);
}
return null;
}
}
Then the unit test should test the fallback default json string.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {"server.port=0"})
#ExtendWith(SpringExtension.class)
#DirtiesContext
class AlbumServiceTest {
#Autowired
AlbumService albumService;
#Test
void getAlbumListFallBack() {
String result = albumService.getAlbumList(null);
assertNotNull(result);
assertTrue(result.length() > 0);
assertEquals(
"[\n" +
" {\n" +
" \"userId\": 1,\n" +
" \"id\": 1,\n" +
" \"title\": \"quidem molestiae enim\"\n" +
" },\n" +
" {\n" +
" \"userId\": 1,\n" +
" \"id\": 2,\n" +
" \"title\": \"sunt qui excepturi placeat culpa\"\n" +
" }\n" +
"]",
result);
}
}

Related

Spring WebFlux endpoint performance logger

Do you know any good practices to log Spring WebFlux controller endpoint performance to keep reactive nature? Seems the following principle wouldn't work because it will block the IO as the ProceedingJoinPoint doesn't return Publisher<> , it returns just an object
#Aspect
#Component
#Slf4j
public class LoggingAspect
{
//AOP expression for which methods shall be intercepted
#Around("execution(* com.company.service..*(..)))")
public Object profileAllMethods(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
{
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
//Get intercepted method details
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
final StopWatch stopWatch = new StopWatch();
//Measure method execution time
stopWatch.start();
Object result = proceedingJoinPoint.proceed();
stopWatch.stop();
//Log method execution time
log.info("Execution time of " + className + "." + methodName + " :: " + stopWatch.getTotalTimeMillis() + " ms");
return result;
}
}
Actually as mentioned #Toerktumlare in the comments proceedingJoinPoint.proceed() returns the type of object whatever is you controller endpoint return type, so it is possible to keep reactive nature. Note .subscribeOn(Schedulers.parallel()) is optional here, that is for my back code to support parallelism. Posting the solution for this:
#Aspect
#Component
#Slf4j
public class LoggingAspect
{
//AOP expression for which methods shall be intercepted
#Around("execution(* com.company.service..*(..)))")
public Object logEndpointPerformance(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
{
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
//Get intercepted method details
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
final StopWatch stopWatch = new StopWatch();
//Measure method execution time
stopWatch.start();
Object result = proceedingJoinPoint.proceed();
if(result instanceof Mono){
return ((Mono)result).subscribeOn(Schedulers.parallel()).flatMap(r -> {
logExecutionTime(className, methodName, stopWatch);
return Mono.just(r);
});
}
else if(result instanceof Flux){
return ((Flux<Object>)result).subscribeOn(Schedulers.parallel()).collectList().flatMapMany(r -> {
logExecutionTime(className, methodName, stopWatch);
return Flux.fromIterable(r);
});
}
else{
logExecutionTime(className, methodName, stopWatch);
return result;
}
}
private void logExecutionTime(final String className, final String methodName, final StopWatch stopWatch){
stopWatch.stop();
//Log method execution time
log.debug("[ " + stopWatch.getTotalTimeMillis() + " mls ] lasted execution of" + className + "." + methodName );
}
}

Consuming Soap Service in spring boot application

I need to consume a soap service in spring boot. How can i do that easily using annotations like we do for Rest. I need to send headers, form the body for my service. Please help me with the solution
public String sendMessage(String processInstanceId) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
String request = "<SOAP:Envelope xmlns:" + "SOAP='http://schemas.xmlsoap.org/soap/envelope/'>" + "<SOAP:Body>"
+ "<SendMessage xmlns='http://schemas.cordys.com/bpm/execution/1.0'>" + "<receiver>" + processInstanceId
+ "</receiver>" + "<message overwrite='false' />" + "</SendMessage>" + "</SOAP:Body>"
+ "</SOAP:Envelope>";
SendMessageAPI sendMessageObject = new SendMessageAPI();
StreamSource source = new StreamSource(new StringReader(request));
StreamResult result = new StreamResult(System.out);
System.out.println("called service" + request);
webServiceTemplate.sendSourceAndReceiveToResult(
"url",
source, result);
return "Success";
You may use Spring Web Service where it's present the WebServiceTemplate similar to the RestTemplate
In order to add SOAP Header and/or HTTP Header you can implement the WebServiceMessageCallback interface.
Here a simple example for adding HTTP Headers
The WebServiceMessageCallback implementation (note I'm using Axiom as MessageFactory)
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
private String headerKey;
private String headerValue;
private String soapAction;
public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
{
super();
this.headerKey = headerKey;
this.headerValue = headerValue;
this.soapAction = soapAction;
}
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
validateRequiredFields();
addRequestHeader(headerKey, headerValue);
if (StringUtils.hasText(this.soapAction))
{
AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
axiomMessage.setSoapAction(this.soapAction);
}
}
private void addRequestHeader(String headerKey, String headerValue)
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpComponentsConnection)
{
HttpComponentsConnection conn = (HttpComponentsConnection) connection;
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
else if( connection instanceof ClientHttpRequestConnection )
{
ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
}
}
}
The WebServiceMessageCallback usage:
WebServiceResponse resp = (WebServiceResponse)webSvcTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, "http://ws.com/soapAction") );
I hope it's usefull
Angelo

Get results from Spring call of Mongo MapReduce

I try to get the results of my mapReduce function on MongoDB directly after the call of my function in my Java Spring code :
MongoOperations mongoOperations = new MongoTemplate(new SimpleMongoDbFactory(new Mongo("xxx.xxx.xxx.xxx"), "xxx"));
MapReduceResults<Object> results = mongoOperations.mapReduce(
"counters",
mapFunctionCounters,
reduceFunctionCounters,
new MapReduceOptions().scopeVariables(scopeVariables).outputTypeInline(),
Object.class);
String jsonString = "";
ObjectMapper mapper = new ObjectMapper();
try {
jsonString = mapper.writeValueAsString(results);
System.out.println("jsonString = " + jsonString);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Console result :
jsonString = {"rawResults":null,"outputCollection":null,"timing":{"mapTime":-1,"emitLoopTime":-1,"totalTime":576},"counts":{"inputCount":100287,"emitCount":7102,"outputCount":104}}
The mapReduce works well when I specify a collection output, but I don't understand how to get the results directly like with the out: {inline: 1} property used in the mongodb command.
Someone to help me please?
I just had to add the following class :
#AllArgsConstructor
#Setter
#Getter
public class ValueObject {
private int id;
private float value;
#Override
public String toString() {
return "ValueObject [id=" + id + ", value=" + value + "]";
}
}
Then change the way to display the result :
MapReduceResults<ValueObject> results = mongoOperations.mapReduce(
"counters",
mapFunctionCounters,
reduceFunctionCounters,
new MapReduceOptions().scopeVariables(scopeVariables).outputTypeInline(),
ValueObject.class
);
for (ValueObject valueObject : results) {
System.out.println(valueObject.getId());
}

#Around advice returning correct response but at client side response is null or undefined

I am trying to apply Around advice to my "Login.jsp" with angular js. And the problem is my controller method is check and I am applying around advice to check method but when I run my application I will get undefined as response at Login.jsp. And but the result which I had printed in my advice contains expected result.But I am not getting it on client side.
AroundAdvice.java
#Aspect #Component
public class AroundAdvice {
static Logger log = Logger.getLogger(AfterLoginAspect.class.getName());
#Around("execution(* com.admin.controller.LoginController.check(..))")
public void logWrittter(ProceedingJoinPoint jp) throws Throwable {
SimpleDateFormat date=new SimpleDateFormat();
log.info("Date Time :: " + date.format(new Date().getTime()));
Object result = jp.proceed();
System.out.println("result around");
log.info("result :: " + result);
// returns {"get Status":"home"}
}
}
LoginController.jsp
// authentication check
#RequestMapping(value = "/PostFormData", method = RequestMethod.POST)
public #ResponseBody JSONObject check(#RequestBody LoginBo login) {
System.out.println("checkCredentials::" + login.getUserName());
String username = login.getUserName();
// log.info("uswername ::"+username);
JSONObject result = new JSONObject();
String encrptedpassword = encryptdPwd.encrypt(login.getPassWord());
boolean login_status = loginService.checkCredentials(username, encrptedpassword);
// log.info("login_status ::"+login_status);
// System.out.println("staus ::"+login_status);
if (login_status == true && login.isIs_system_generated_pwd() == true) {
System.out.println("sys gen chnge pwd:: " + login.isIs_system_generated_pwd());
result.put("getStatus", "change");
// System.out.println(resultPage);
// login.setIs_system_generated_pwd(false);
} else if (login_status == true && login.isIs_system_generated_pwd() == false) {
result.put("getStatus", "home");
// System.out.println("Home paege ");
} else {
result.put("getStatus", "error");
}
System.out.println("result ::" + result);
// log.info("result ::"+resultPage);
return result;
}
Your pointcut does not match because the advice has a void return type, but your method returns a JSONObject. So maybe you want to change your advice declaration to:
#Aspect #Component
public class AroundAdvice {
static Logger log = Logger.getLogger(AfterLoginAspect.class.getName());
#Around("execution(* com.admin.controller.LoginController.check(..))")
public JSONObject logWriter(ProceedingJoinPoint jp) throws Throwable {
SimpleDateFormat date=new SimpleDateFormat();
log.info("Date Time :: " + date.format(new Date().getTime()));
JSONObject result = (JSONObject) jp.proceed();
System.out.println("result around");
log.info("result :: " + result);
return result;
}
}
Please note
public JSONObject logWriter instead of public void logWrittter,
JSONObject result = (JSONObject) jp.proceed(); instead of Object result = jp.proceed(); and
return result; instead of no return value.

Logging request and response for Spring mvc Service

hi All , i Have to log all the request , response , Exception ,
Errors for my Spring services. i had searched about the
interceptors , filters , logging filters , Spring interceptors :
HandlerInterceptorAdapter logger filters :
AbstractRequestLoggingFilter.java and its sub classes
(http://www.javawebdevelop.com/1704067/),
CommonsRequestLoggingFilter.java filters : LoggerFilter.
can any body its difference, and the best approach to do so i am
confused or i need to find out the third party library to do this
?..
I used AOP for something similar in one project. I had to write a class like this:
#Component
#Aspect
public class RequestMonitor {
private static final Logger logger = LoggerFactory.getLogger(RequestMonitor.class);
#Around("#annotation(org.example.ToBeLogged)")
public Object wrap(ProceedingJoinPoint pjp) throws Throwable {
logger.info("Before controller method " + pjp.getSignature().getName() + ". Thread " + Thread.currentThread().getName());
Object retVal = pjp.proceed();
logger.info("Controller method " + pjp.getSignature().getName() + " execution successful");
return retVal;
}
}
Log4j is a java based logging utility with easy integration with Spring MVC. Log4j comes with different logging levels to allow for appropriate logging in correspondence to the development environment.
Log4j 2 is the successor to log4j and has better performance as compared to its predecessor.
Kindly refer the following link for an integration of spring MVC + Log4j
http://www.codejava.net/frameworks/spring/how-to-use-log4j-in-spring-mvc
Edit: As mentioned in comment , PFB the code for logging both request and response.
#Aspect
#Component
public class ResponseLoggerAspect {
private static final Logger logger = Logger.getLogger("requestResponseLogger");
ExclusionStrategy excludeJsonAnnotation = new JsonIgnoreAnnotationExclusionStrategy();
Gson gson = new GsonBuilder().setExclusionStrategies(excludeJsonAnnotation).create();
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controller() {}
#Pointcut("execution(* *(..))")
public void method() {}
#Pointcut("execution(#com.company.annotation.AddLog * *(..))")
public void Loggable() {}
//This will be caught for only those controller method where #AddLog annotation is written
#Before("Loggable()")
public void printRequestLog(JoinPoint joinPoint) {
try {
Object[] argsList = joinPoint.getArgs();
String str = "[";
for(Object arg : argsList) {
if(arg instanceof Object[]) {
str += Arrays.toString((Object[])arg) + ", ";
} else {
str += String.valueOf(arg) + ", ";
}
}
str += "]";
logger.info("Request args for " + joinPoint.getSignature().getName() + " are : " + str);
} catch(Exception ex) {
logger.info("Unable to log request args", ex);
}
}
//This will be called for all controller methods after returning
#AfterReturning(pointcut = "controller() && method()", returning="result")
public void afterReturning(JoinPoint joinPoint , Object result) {
long start = System.nanoTime();
try {
logger.info("Response sent by " + joinPoint.getSignature().getName() + " are : " + gson.toJson(result));
} catch(Exception ex) {
logger.error("Returned result cant be converted in JSON " , ex);
}
long end = System.nanoTime();
logger.info("elapsed time : " + (end - start));
}
}

Resources