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

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.

Related

JUnit4 with Mockito for unit testing

public class DgiQtyAction extends DispatchAction {
private final Logger mLog = Logger.getLogger(this.getClass());
public ActionForward fnDgiQty(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
mLog.debug(request.getParameter(EcoConstants.ecopidid));
ActionErrors errorMessage = new ActionErrors();
if(request.getSession().getAttribute(EcoConstants.userBean)==null)
{
request.setAttribute(EcoConstants.ERROR_MESSAGE,EcoConstants.SESSION_TIMEOUT);
errorMessage.add(Globals.MESSAGE_KEY, new ActionMessage(EcoConstants.error_message,EcoConstants.SESSION_TIMEOUT));
saveMessages(request, errorMessage);
request.setAttribute(EcoConstants.errorMessageType,EcoConstants.errorMessageType);
return mapping.findForward(EcoConstants.SESSION_FORWARD);
}
String ecoPidID = (String) request.getParameter(EcoConstants.ecopidid);
String pidId = ESAPI.encoder().encodeForHTML((String) request.getParameter(EcoConstants.pidid));
mLog.debug("pidid --------" + pidId);
mLog.debug("ecopidpid --------" + ecoPidID);
ArrayList dgiQty = new ArrayList();
NeedDgiForm niForm = new NeedDgiForm();
try {
NeedDgiBD niBD = new NeedDgiBD();
if (ecoPidID != null) {
dgiQty = niBD.getDgiQty(ecoPidID);
if (dgiQty.size() != 0) {
mLog.debug(dgiQty.get(0).toString());
if (dgiQty.get(0).toString().equals(EcoConstants.hundred)) {
niForm.setGlqtyList(new ArrayList());
request.setAttribute(EcoConstants.pidId, pidId);
return mapping.findForward(EcoConstants.SuccessInfo);
} else {
mLog.debug("fnBug 1----------------> " + dgiQty.get(0));
mLog.info("dgiQty sizeeeee: :" + dgiQty.size());
niForm.setGlqtyList(dgiQty);
}
}
}
} catch (Exception e) {
//log.error("General Exception in DgiQtyAction.fnDgiQty: "
// + e.getMessage(), e);
request.setAttribute(EcoConstants.ERROR_MESSAGE, e.getMessage());
return mapping.findForward(EcoConstants.ERROR_PAGE);
}
mLog.debug("pidid after wards--------" + pidId);
request.setAttribute(EcoConstants.pidId, pidId);
request.setAttribute(mapping.getAttribute(), niForm);
return mapping.findForward(EcoConstants.SuccessInfo);
}
}
public class DgiQtyActionTest {
ActionMapping am;
ActionForm af;
DgiQtyAction dat;
private MockHttpSession mocksession;
private MockHttpServletRequest mockrequest;
private MockHttpServletResponse mockresponse;
#Test
public void fnDgiQty() throws Exception
{
mocksession = new MockHttpSession();
mockrequest = new MockHttpServletRequest();
mockresponse = new MockHttpServletResponse();
((MockHttpServletRequest) mockrequest).setSession(mocksession);
mocksession.setAttribute("userBean","userBean");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockrequest));
mockrequest.addParameter("ecopid","something");
mockrequest.addParameter("pid","<script>");
Encoder instance = ESAPI.encoder();
assertEquals("something",mockrequest.getParameter("ecopid"));
assertEquals("<script>",instance.encodeForHTML(mockrequest.getParameter("pid")));
dat=mock(DgiQtyAction.class);
am=mock(ActionMapping.class);
af=mock(ActionForm.class);
dat.fnDgiQty(am,af,mockrequest, mockresponse);
}
}
I wrote the unit test case for above class. i ran this code through jenkins and used sonarqube as code coverage.I need to cover the ESAPi encoder for the parameter, it got build success but the coverage percentage doesn't increase. i couldn't found the mistake in it. pls help me guys. Thanks in Advance

#Async is not working for one of my method

I think when I add #Async notation for this method, it returns an empty result. The method is to get data from a website and return the data. It works when I remove #Async. When I am not using #Async, it is using a thread called "http-nio-8080-exec-1", when I use #Async, it is using a thread with my naming prefix "My-thread1". I do not know if I need to config anywhere else like xml or something. Thank you!
#Async
public CompletableFuture<List<Post>> searchByTag(String[] tags, String sortBy, String direction ) throws ExecutionException, InterruptedException, IOException {
logger.info("I am here---------------------------------------------------------- ");
if(tags == null || tags.length == 0){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST , "Tags parameter is required");
}
if(sortBy == null){
sortBy = "id";
} else if(!sortBy.equals("id") && !sortBy.equals("reads") && !sortBy.equals("likes") && !sortBy.equals("popularity")){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "sortBy parameter is invalid");
}
if(direction == null){
direction = "asc";
} else if(!direction.equals("asc") && !direction.equals("desc")){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "direction parameter is invalid");
}
long start = System.currentTimeMillis();
Set<Post> postSet = new HashSet<Post>();
String baseUrl = "https://api.hatchways.io/assessment/blog/posts?tag=";
HttpClient client = HttpClient.newHttpClient();
// send a get request to get all the posts with certain tag
for(int i = 0; i < tags.length; i++){
String url = baseUrl + tags[i];
HttpRequest request = HttpRequest.newBuilder()
.GET()
.header("Accept", "application/json")
.uri(URI.create(url))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
// HttpResponse<String> result = response.get();
String responseBody = response.body().substring(9, response.body().length()-1);
logger.info("Using Thread: " + Thread.currentThread().getName());
List<Post> posts = mapper.readValue(responseBody, new TypeReference<List<Post>>() {});
// put all the posts into a set to filter out all the repeated posts
postSet.addAll(posts);
}
ArrayList<Post> postList = new ArrayList<>(postSet);
sortThePosts(sortBy, direction, postList);
long end = System.currentTimeMillis();
logger.info("Total time: " + Long.toString(end-start));
return CompletableFuture.completedFuture(postList);
}
Following is my config:
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
#Configuration
#EnableAsync
public class AsyncConfig {
#Bean(name = "threadPoolTaskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(5);
threadPoolTaskExecutor.setQueueCapacity(20);
threadPoolTaskExecutor.setThreadNamePrefix("My_Thread");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
The configuration looks fine. Have you created searchByTag() method in the same class you are calling the method from? If so try creating a separate service class that will contain your Async methods.

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 );
}
}

Invoke lambda function handler java

I have a lambda function which has a handler which inturn has multiple routers. Each router corresponds to an API.
I have created a lambda client in java and need to call those APIs. To call these APIs, I need to invoke the handler and pass a payload to the client along with it. Can you guys help me with the syntax for invoking the handler and passing the payload.
If I understand your question correctly I first created a Lambda that looked like:
public class SampleHandler implements RequestStreamHandler {
private static final Logger logger = LogManager.getLogger(SampleHandler.class);
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
logger.info("handlingRequest");
LambdaLogger lambdaLogger = context.getLogger();
ObjectMapper objectMapper = new ObjectMapper();
String inputString = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
JsonNode jsonNode = objectMapper.readTree(inputString);
String route = jsonNode.get("route").asText();
RouterResult routerResult = new RouterResult();
switch( route ) {
case "requestTypeA":
RequestTypeA requestTypeA = objectMapper.readValue(inputString, RequestTypeA.class);
routerResult.setResult(handleRequestTypeA(requestTypeA));
break;
case "requestTypeB":
RequestTypeB requestTypeB = objectMapper.readValue(inputString, RequestTypeB.class);
routerResult.setResult(handleRequestTypeB(requestTypeB));
break;
default:
logger.error( "don't know how to handle route of type \"" + route + "\n" );
routerResult.setResult("error");
}
outputStream.write(objectMapper.writeValueAsString(routerResult).getBytes(StandardCharsets.UTF_8));
logger.info("done with run, remaining time in ms is " + context.getRemainingTimeInMillis() );
}
private String handleRequestTypeA(RequestTypeA requestTypeA) {
logger.info("handling requestTypeA, requestTypeA.requestA is " + requestTypeA.getRequestA() );
return "handled requestTypeA";
}
private String handleRequestTypeB(RequestTypeB requestTypeB) {
logger.info("handling requestTypeB, requestTypeB.requestB is " + requestTypeB.getRequestB() );
return "handled requestTypeB";
}
}
with RouterRequest.java:
public class RouterRequest {
protected String route;
public String getRoute() {
return route;
}
}
and RequestTypeA.java:
public class RequestTypeA extends RouterRequest {
private String requestA;
public RequestTypeA() {
route = "requestTypeA";
}
public String getRequestA() {
return requestA;
}
public void setRequestA(String requestA) {
this.requestA = requestA;
}
}
and RequestTypeB.java
public class RequestTypeB extends RouterRequest {
private String requestB;
public RequestTypeB() {
route = "requestTypeB";
}
public String getRequestB() {
return requestB;
}
public void setRequestB(String requestB) {
this.requestB = requestB;
}
}
And a result class, RouterResult.java:
public class RouterResult {
private String result;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
#Override
public String toString() {
return "RouterResult{" +
"result='" + result + '\'' +
'}';
}
}
Then, to invoke this Lambda, you will need a role that has the lambda:InvokeFunction permission. The code to invoke looks like:
public class RouterRunner {
private static final String AWS_ACCESS_KEY_ID = "<access key>";
private static final String AWS_SECRET_ACCESS_KEY = "<access secret>";
public static void main( String[] argv ) throws IOException {
AWSCredentials credentials = new BasicAWSCredentials( AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY );
AWSLambda lambda = AWSLambdaClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.withCredentials(new AWSStaticCredentialsProvider(credentials)).build();
RequestTypeA requestTypeA = new RequestTypeA();
requestTypeA.setRequestA("set from the runner, request type A");
ObjectMapper objectMapper = new ObjectMapper();
InvokeRequest invokeRequest = new InvokeRequest()
.withFunctionName("lambda-router")
.withPayload(objectMapper.writeValueAsString(requestTypeA));
invokeRequest.setInvocationType(InvocationType.RequestResponse);
InvokeResult invokeResult = lambda.invoke(invokeRequest);
String resultJSON = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8);
System.out.println( "result from lambda is " + resultJSON );
RouterResult routerResult = objectMapper.readValue(resultJSON, RouterResult.class);
System.out.println( "result.toString is " + routerResult.toString() );
RequestTypeB requestTypeB = new RequestTypeB();
requestTypeB.setRequestB("set from the runner, request type B");
invokeRequest = new InvokeRequest()
.withFunctionName("lambda-router")
.withPayload(objectMapper.writeValueAsString(requestTypeB));
invokeRequest.setInvocationType(InvocationType.RequestResponse);
invokeResult = lambda.invoke(invokeRequest);
resultJSON = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8);
System.out.println( "result from lambda is " + resultJSON );
routerResult = objectMapper.readValue(resultJSON, RouterResult.class);
System.out.println( "result.toString is " + routerResult.toString() );
}
}
There likely needs to be some error handling improvement and I'm sure you could make this a bit more efficient. But that's the overall idea. Ultimately on the Lambda side I convert the InputStream to a String and convert that String to some sort of object based on a common field in the request types. On the client side I convert the objects into JSON, send them out, and convert the result back from a JSON String back into the result object.

How to make queryparams mandatory in Java Jersey REST services?

I have a REST API that accepts 3 query params. When the query is called without any one of the query parameters, the API executes and returns the result. How do we make the queryparams mandatory? How can I add validation to check if all the parameters are present? Also, please let me know the best approach.
On a very simple level you could just inject the HttpServletRequest and check yourself:
#GET
public Response example(#Context HttpServletRequest request,
#QueryParam("name") String name) {
if (null == request.getParameter("name")) {
ResponseBuilder builder = Response.status(404);
return builder.build();
}
// Do something with name
}
Or you can implement something more elaborate using AOP. Here's a blog post about further options.
jersey doesn't give a mandatory parameter checking functionality out of the box. however you can do something like implementing your own annotation to achieve it.
Below is the annotation code:
#Target(value = ElementType.METHOD)
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Required {
String[] value();
}
You also need a filter, below is the code:
public class RequiredParamResourceFilterFactory implements ResourceFilterFactory {
#Context
private transient HttpServletRequest servletRequest;
private class RequiredParamFilter implements ResourceFilter, ContainerRequestFilter {
private final String[] requiredParams;
protected List<String> parametersValueMissing;
private RequiredParamFilter(String[] requiredParams) {
this.requiredParams = requiredParams;
}
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
boolean missingMandatoryParameter = false;
List<String> missingParameters = new ArrayList<String>();
List<String> requiredParametersValueMissing = new ArrayList<String>();
List<String> URLParameters = getURLParameters(containerRequest.getQueryParameters());
List<String> methodRequiredParameters = Arrays.asList(requiredParams);
if (methodRequiredParameters != null) {
for (String methodRequiredParam : methodRequiredParameters) {
if (URLParameters == null) {
missingMandatoryParameter = true; //we will check this flag before returning result set to caller
missingParameters.add(methodRequiredParam);
} else if (!URLParameters.contains(methodRequiredParam)) {
missingMandatoryParameter = true; //we will check this flag before returning result set to caller
missingParameters.add(methodRequiredParam);
//Add to required parameters value missing List, only if the parameter is mandatory and value is not provided
// in the URL
} else if (parametersValueMissing.contains(methodRequiredParam)) {
requiredParametersValueMissing.add(methodRequiredParam);
}
}
if (missingMandatoryParameter && requiredParametersValueMissing.size() > 0) {
throw new YourCustomException("Missing Parameters = " + StringHelper.ArrayToString(missingParameters) +
"\nParameter value missing for " + StringHelper.ArrayToString(requiredParametersValueMissing));
} else if (missingMandatoryParameter) {
throw new YourCustomException("Missing Parameters = " + StringHelper.ArrayToString(missingParameters), MisbarErrorCode.VALIDATION_WRONG_INPUT_ERROR, "Customers");
} else if (requiredParametersValueMissing != null &&
requiredParametersValueMissing.size() > 0) {
throw new YourCustomException("Parameter value missing for " + StringHelper.ArrayToString(requiredParametersValueMissing));
}
}
return containerRequest;
}
#Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
/**
* To fetch the parameters sent to webservice call, these will be used to find if required parameter
* are present or not
*
* #param queryParams the queryparams sent
* #return all the parameters sent in URL
*/
private List<String> getURLParameters(MultivaluedMap<String,String> queryParams) {
parametersValueMissing = new ArrayList<String>();
List<String> arr = new ArrayList<String>();
for(String key:queryParams.keySet())
{
arr.add(key);
if(queryParams.get(key)==null)
parametersValueMissing.add(key);
}
if(!arr.isEmpty())
return arr;
return null;
}
}
#Override
public List<ResourceFilter> create(AbstractMethod am) {
Required required = am.getAnnotation(Required.class);
if(required!=null)
{
return Collections.<ResourceFilter>singletonList(new RequiredParamFilter(required.value()));
}
return null;
}
}
Below sample shows how to use this annotation, so in below webservice; file_id and count are mandatory parameters:
#GET
#Produces(MediaType.APPLICATION_JSON+";charset=utf-8")
#Cacheable(isCacheable = true)
#Path("posts/clusters")
#Required({"file_id","count"})
#Timed
public Response getClusters(
#QueryParam("file_id") Integer fileId,
#QueryParam("count") Integer count,
#DefaultValue("-1")#QueryParam("start_time") Long startTime){
;
}
If mandatory parameters are not provided in webservice call, you receive an error like below, mentioning the parameter names that are missing:
{
message: "Missing Parameters = file_id, count",
errorCode: "600"
}
Hope this solves your problem.

Resources