class SpringCallMetadataInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        Authentication delegate = SecurityContextHolder.getContext().getAuthentication();
        if (delegate == null || delegate instanceof CallMetadataAuthentication) {
            return super.preHandle(httpServletRequest, httpServletResponse, handler);
        }

        String callReason = httpServletRequest.getHeader(CallMetadataHeaders.CALL_REASON_HEADER);
        String debugQueryParameter = httpServletRequest.getParameter(DEBUG_QUERY_PARAM);
        boolean debug = debugQueryParameter == null
                ? Boolean.parseBoolean(httpServletRequest.getHeader(CallMetadataHeaders.DEBUG_HEADER))
                : Boolean.parseBoolean(debugQueryParameter);

        String originalCallerId = httpServletRequest.getHeader(CallMetadataHeaders.CALLER_ID_HEADER);

        Caller directCaller = getDirectCaller(httpServletRequest, delegate);

        CallMetadata.Builder callMetadataBuilder = CallMetadata.newBuilder()
                .withCallReason(callReason)
                .withDebug(debug);

        if (originalCallerId == null) {
            callMetadataBuilder.withCallers(Collections.singletonList(directCaller));
        } else {
            CallerType originalCallerType = CallerType.parseCallerType(
                    originalCallerId,
                    httpServletRequest.getHeader(CallMetadataHeaders.CALLER_TYPE_HEADER)
            );
            Caller originalCaller = Caller.newBuilder()
                    .withId(originalCallerId)
                    .withCallerType(originalCallerType)
                    .build();

            callMetadataBuilder.withCallers(asList(originalCaller, directCaller));
        }

        CallMetadataAuthentication authentication = new CallMetadataAuthentication(callMetadataBuilder.build(), delegate);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        return super.preHandle(httpServletRequest, httpServletResponse, handler);
    }

}