WebClientInvocationHandler.java

/*
 * Copyright 2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bremersee.web.reactive.function.client.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodyUriSpec;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;

/**
 * The web client invocation handler.
 *
 * @author Christian Bremer
 */
@Slf4j
class WebClientInvocationHandler implements InvocationHandler {

  private final Map<MethodDescription, InvocationFunctions> methodFunctions;

  private final InvocationFunctions commonFunctions;

  private final WebClient webClient;

  private final Class<?> targetClass;

  /**
   * Instantiates a new web client invocation handler.
   *
   * @param methodFunctions the method functions
   * @param commonFunctions the common functions
   * @param webClient the web client
   * @param targetClass the target class
   */
  WebClientInvocationHandler(
      final Map<MethodDescription, InvocationFunctions> methodFunctions,
      final InvocationFunctions commonFunctions,
      final WebClient webClient,
      final Class<?> targetClass) {
    this.methodFunctions = methodFunctions;
    this.commonFunctions = commonFunctions;
    this.webClient = webClient;
    this.targetClass = targetClass;
  }

  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args) {
    if (ReflectionUtils.isObjectMethod(method)) {
      if (ReflectionUtils.isEqualsMethod(method)) {
        return this.equals(args[0]);
      } else if (ReflectionUtils.isHashCodeMethod(method)) {
        return this.hashCode();
      } else if (ReflectionUtils.isToStringMethod(method)) {
        return "WebClient Proxy of " + (targetClass != null ? targetClass.getName() : "null");
      } else {
        return ReflectionUtils.invokeMethod(method, this, args);
      }
    }
    final InvocationParameters parameters = new InvocationParameters(targetClass, method, args);
    final InvocationFunctions functions = InvocationFunctions.merge(
        commonFunctions,
        methodFunctions.get(new MethodDescription(method)));
    final RequestHeadersUriSpec<?> uriSpec = functions.getUriSpecBuilder()
        .build(parameters, webClient);
    uriSpec
        .uri(uriBuilder -> functions.getUriBuilder().build(parameters, uriBuilder))
        .headers(
            httpHeaders -> functions.getHeadersBuilder().build(parameters, httpHeaders))
        .cookies(cookies -> functions.getCookiesBuilder().build(parameters, cookies));
    if (uriSpec instanceof RequestBodyUriSpec) {
      functions.getBodyInserter().insert(parameters, (RequestBodyUriSpec) uriSpec);
    }
    final ResponseSpec responseSpec = uriSpec.retrieve();
    responseSpec.onStatus(functions.getErrorDetector(), functions.getErrorDecoder());
    return functions.getResponseBuilder().build(parameters, responseSpec);
  }
}