0

I have an interesting problem with me (at least it is a problem from my current point of view).

I have a RESTful web service that is exposing an interface. In our environment, we use annotation to decorate the request handlers, e.g. -

@RequiredTokenType("Binary")
public String getRecordInfo(HttpServletRequest request) {
    String recordInfo = null;

    final String methodName = "getRecordInfo()";
    _log.debug("{}: received request", methodName);

    // validate the request information
    ValidationUtils.validateAcceptHeader(request, MimeConstants.getContentType());

    .   .   .
    .   .   .

    return recordInfo;
}

We need to have a CXF interceptor/aspect (spring AOP) to execute before the handler (like the above), and check the HttpServletRequest for token type. If the token type (or any other attribute that is decorating the handler) is not of type specified in the annotation, stop the execution and return HTTP status 400 (bad request). There are about 20 request handlers like the above.

The problem I am facing here is that after writing a spring AOP based around aspect, (like below) I am able to catch the request before getRecordInfo() executes, but when I try to return 400 (or) throw an exception, the HTTP client still sees 200 -

public void validateRequest(ProceedingJoinPoint joinPoint,
                            HttpServletRequest httpRequest,
                            RequiredTokenType tokenType) throws Throwable {

    final String methodName = "validateRequest()";
    _logger.info("{}: Entered, Thread Id: {}", methodName, "" + Thread.currentThread().getId());

    // Extract the *REQUIRED* headers from the request ...
    final String tokenData = httpRequest.getHeader(HEADER_TOKEN_DATA);

    if (tokenData == null || tokenData.trim().length() < MIN_POSSIBLE_TOKEN_SIZE) {
        // Error condition .... return (400 Bad Request)
        _logger.info("{}: Invalid token. The HTTP request is rejected in lack of a valid token.");

        throw new MissingTokenException(HttpStatus.BAD_REQUEST,
                                        ErrorCode.BAD_REQUEST,
                                        "Token is missing from the request.");
    }

    ValidityToken extractedTokenFromRequest = ValidityToken.initializeFromBase64(tokenData);

    // Get the type of the token this request must include ...
    String decoratedTokenType = tokenType.value();
    _logger.debug("{}: Token Type Required: ", methodName, decoratedTokenType);

    if (! extractedTokenFromRequest.getTypeName().equals(decoratedTokenType)) {
        // Error condition .... return (400).
        _logger.info("{}: {}",
                     methodName,
                     "The token in the request mismatches the type specified in RequiredTokenType handler. 400 Bad Request.");
        throw new TokenTypeMismatchException(HttpStatus.BAD_REQUEST,
                                             ErrorCode.BAD_REQUEST,
                                             "Token type doesn't match.");
    }

    // More validations on extractedTokenFromRequest
    .   .   .
    .   .   .

    // Continue with the actual business logic if correct token is included ...
    joinPoint.proceed();
}

I have checked the log file and I can see the following log entries that confirm that both the request handler and the aspect are being called -

    getRecordInfo(): received request
    .   .   .
    .   .   .
    validateRequest(): Entered, Thread Id: 792
    .   .   .
    .   .   .
    validateRequest(): Invalid token. The HTTP request is rejected in lack of a valid token.

Despite the message, still the client see a 200 and server log shows evidence of other CXF based interceptors executing.

Here is the Spring context XML that is defining the pointcut -

<bean id="vGateKeeper" class="com....core.RequestValidator" />


<aop:config>
    <aop:aspect id="methodInvocation" ref="methodInvocationProfiler">
        <aop:around method="profileMethodInvocation"
                    pointcut="execution(* org.springframework.orm.jpa.JpaTransactionManager.commit(..))"/>
        <aop:around method="profileMethodInvocation"
                    pointcut="execution(* org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(..))"/>
    </aop:aspect>
    <aop:aspect id="gateKeeper" ref="vGateKeeper">
        <aop:pointcut expression="execution(* getRecordInfo(..)) and args(httpRequest) and @annotation(modPerms)" id="my"/>
        <aop:around pointcut-ref="my" method="validateRequest"/>
    </aop:aspect>
</aop:config>

How can Spring AOP aspect can be used to return HTTP 400 in this case, cancelling execution of any other interceptor ?

I was also looking at writing an Apache CXF interceptor to catch the call and return 400 before it reaches the request handler, but then I am not sure, How using an interceptor I can can know which request handler was suppose to execute and what annotation is decorating the request handler. Does CXF interceptor offer any way to know, which request handler will eventually execute ?

I was looking here, (https://cxf.apache.org/docs/interceptors.html) but didn't find anyway.

Any help is appreciated.

Regards,

(*Vipul)() ;

sgsi
  • 382
  • 1
  • 8
  • 18

1 Answers1

2

Actually it is rather funny that you ask the community why your advice does not work, but do not post the actual advice code. So all that is left for me to do here is guesswork. I can think of two things:

  • Your advice returns an int, but the original code returns a String. Actually an @Around advice should return the same type as the wrapped/intercepted method (or Object).
  • Your advice calls proceed(), but you do not want the original method to be executed and should not call proceed(), but return your own result.

In any case, if my two guesses are wrong, please update your question so maybe I can update my answer or someone else with CXF knowledge (which I have not, I am rather an AOP expert) can find a way for you to do it without AOP.

P.S.: One more question: Have you tested if the advice is actually fired, e.g. by writing log messages inside the advice?

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks kriegaex, I have added full code of aspect. I have also changed the return type to void, since it wasn't actually suppose to return anything. – sgsi May 06 '14 at 10:45
  • I actually checked the logs and I found that both the request handler and the Around advice is executing. The advice is also entering in the desired code block and it says, it is rejecting the request with a 400 status. The code code throws an exception after this log message, but what I am seeing here is an HTTP 200 at client side. We also have additional CXF interceptors active on the request handler. Basically, I think throwing exception is not terminating the CXF interceptors from being invoked. The 200 is being returned by interceptors. – sgsi May 06 '14 at 10:50
  • 1
    You omitted the pointcut for your request. I would be interested in seeing that one too. BTW, I have added Java syntax highlighting to your sample code. – kriegaex May 06 '14 at 15:39
  • Another idea: I have never used CXF, as I said, and never done web programming, but Google told me that you actually seem to have to wrap your exception into a [Fault](https://cxf.apache.org/javadoc/latest/org/apache/cxf/interceptor/Fault.html) instance and throw that one. You also might want to look into [CheckFaultInterceptor](https://cxf.apache.org/javadoc/latest/org/apache/cxf/binding/soap/interceptor/CheckFaultInterceptor.html). – kriegaex May 06 '14 at 15:56
  • Ah, sorry. I was thinking to add the point cut too, but wasn't sure if that would be useful or not, since I already saw that log entries are being created and thus the around advice is being called. Added it now. – sgsi May 06 '14 at 15:58
  • 1
    Thanks kriegaex, The problem was that the Aspect wasn't returning the value that the JoinPoint was returning. As you expressed in your message, the advise should have same signature as the JoinPoint. Thanks for helping. – sgsi May 09 '14 at 00:39