All Downloads are FREE. Search and download functionalities are using the official Maven repository.

feign.BaseBuilder Maven / Gradle / Ivy

There is a newer version: 13.5
Show newest version
/*
 * Copyright 2012-2023 The Feign 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 feign;

import static feign.ExceptionPropagationPolicy.NONE;
import feign.Feign.ResponseMappingDecoder;
import feign.Logger.NoOpLogger;
import feign.Request.Options;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.querymap.FieldQueryMapEncoder;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public abstract class BaseBuilder, T> implements Cloneable {

  private final B thisB;

  protected final List requestInterceptors =
      new ArrayList<>();
  protected final List responseInterceptors = new ArrayList<>();
  protected Logger.Level logLevel = Logger.Level.NONE;
  protected Contract contract = new Contract.Default();
  protected Retryer retryer = new Retryer.Default();
  protected Logger logger = new NoOpLogger();
  protected Encoder encoder = new Encoder.Default();
  protected Decoder decoder = new Decoder.Default();
  protected boolean closeAfterDecode = true;
  protected boolean decodeVoid = false;
  protected QueryMapEncoder queryMapEncoder = QueryMap.MapEncoder.FIELD.instance();
  protected ErrorDecoder errorDecoder = new ErrorDecoder.Default();
  protected Options options = new Options();
  protected InvocationHandlerFactory invocationHandlerFactory =
      new InvocationHandlerFactory.Default();
  protected boolean dismiss404;
  protected ExceptionPropagationPolicy propagationPolicy = NONE;
  protected List capabilities = new ArrayList<>();


  public BaseBuilder() {
    super();
    thisB = (B) this;
  }

  public B logLevel(Logger.Level logLevel) {
    this.logLevel = logLevel;
    return thisB;
  }

  public B contract(Contract contract) {
    this.contract = contract;
    return thisB;
  }

  public B retryer(Retryer retryer) {
    this.retryer = retryer;
    return thisB;
  }

  public B logger(Logger logger) {
    this.logger = logger;
    return thisB;
  }

  public B encoder(Encoder encoder) {
    this.encoder = encoder;
    return thisB;
  }

  public B decoder(Decoder decoder) {
    this.decoder = decoder;
    return thisB;
  }

  /**
   * This flag indicates that the response should not be automatically closed upon completion of
   * decoding the message. This should be set if you plan on processing the response into a
   * lazy-evaluated construct, such as a {@link java.util.Iterator}.
   *
   * 

* Feign standard decoders do not have built in support for this flag. If you are using this flag, * you MUST also use a custom Decoder, and be sure to close all resources appropriately somewhere * in the Decoder (you can use {@link Util#ensureClosed} for convenience). * * @since 9.6 * */ public B doNotCloseAfterDecode() { this.closeAfterDecode = false; return thisB; } public B decodeVoid() { this.decodeVoid = true; return thisB; } public B queryMapEncoder(QueryMapEncoder queryMapEncoder) { this.queryMapEncoder = queryMapEncoder; return thisB; } /** * Allows to map the response before passing it to the decoder. */ public B mapAndDecode(ResponseMapper mapper, Decoder decoder) { this.decoder = new ResponseMappingDecoder(mapper, decoder); return thisB; } /** * This flag indicates that the {@link #decoder(Decoder) decoder} should process responses with * 404 status, specifically returning null or empty instead of throwing {@link FeignException}. * *

* All first-party (ex gson) decoders return well-known empty values defined by * {@link Util#emptyValueOf}. To customize further, wrap an existing {@link #decoder(Decoder) * decoder} or make your own. * *

* This flag only works with 404, as opposed to all or arbitrary status codes. This was an * explicit decision: 404 -> empty is safe, common and doesn't complicate redirection, retry or * fallback policy. If your server returns a different status for not-found, correct via a custom * {@link #client(Client) client}. * * @since 11.9 */ public B dismiss404() { this.dismiss404 = true; return thisB; } /** * This flag indicates that the {@link #decoder(Decoder) decoder} should process responses with * 404 status, specifically returning null or empty instead of throwing {@link FeignException}. * *

* All first-party (ex gson) decoders return well-known empty values defined by * {@link Util#emptyValueOf}. To customize further, wrap an existing {@link #decoder(Decoder) * decoder} or make your own. * *

* This flag only works with 404, as opposed to all or arbitrary status codes. This was an * explicit decision: 404 -> empty is safe, common and doesn't complicate redirection, retry or * fallback policy. If your server returns a different status for not-found, correct via a custom * {@link #client(Client) client}. * * @since 8.12 * @deprecated use {@link #dismiss404()} instead. */ @Deprecated public B decode404() { this.dismiss404 = true; return thisB; } public B errorDecoder(ErrorDecoder errorDecoder) { this.errorDecoder = errorDecoder; return thisB; } public B options(Options options) { this.options = options; return thisB; } /** * Adds a single request interceptor to the builder. */ public B requestInterceptor(RequestInterceptor requestInterceptor) { this.requestInterceptors.add(requestInterceptor); return thisB; } /** * Sets the full set of request interceptors for the builder, overwriting any previous * interceptors. */ public B requestInterceptors(Iterable requestInterceptors) { this.requestInterceptors.clear(); for (RequestInterceptor requestInterceptor : requestInterceptors) { this.requestInterceptors.add(requestInterceptor); } return thisB; } /** * Sets the full set of request interceptors for the builder, overwriting any previous * interceptors. */ public B responseInterceptors(Iterable responseInterceptors) { this.responseInterceptors.clear(); for (ResponseInterceptor responseInterceptor : responseInterceptors) { this.responseInterceptors.add(responseInterceptor); } return thisB; } /** * Adds a single response interceptor to the builder. */ public B responseInterceptor(ResponseInterceptor responseInterceptor) { this.responseInterceptors.add(responseInterceptor); return thisB; } /** * Allows you to override how reflective dispatch works inside of Feign. */ public B invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { this.invocationHandlerFactory = invocationHandlerFactory; return thisB; } public B exceptionPropagationPolicy(ExceptionPropagationPolicy propagationPolicy) { this.propagationPolicy = propagationPolicy; return thisB; } public B addCapability(Capability capability) { this.capabilities.add(capability); return thisB; } @SuppressWarnings("unchecked") B enrich() { if (capabilities.isEmpty()) { return thisB; } try { B clone = (B) thisB.clone(); getFieldsToEnrich().forEach(field -> { field.setAccessible(true); try { final Object originalValue = field.get(clone); final Object enriched; if (originalValue instanceof List) { Type ownerType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; enriched = ((List) originalValue).stream() .map(value -> Capability.enrich(value, (Class) ownerType, capabilities)) .collect(Collectors.toList()); } else { enriched = Capability.enrich(originalValue, field.getType(), capabilities); } field.set(clone, enriched); } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException("Unable to enrich field " + field, e); } finally { field.setAccessible(false); } }); return clone; } catch (CloneNotSupportedException e) { throw new AssertionError(e); } } List getFieldsToEnrich() { return Util.allFields(getClass()) .stream() // exclude anything generated by compiler .filter(field -> !field.isSynthetic()) // and capabilities itself .filter(field -> !Objects.equals(field.getName(), "capabilities")) // and thisB helper field .filter(field -> !Objects.equals(field.getName(), "thisB")) // skip primitive types .filter(field -> !field.getType().isPrimitive()) // skip enumerations .filter(field -> !field.getType().isEnum()) .collect(Collectors.toList()); } public final T build() { return enrich().internalBuild(); } protected abstract T internalBuild(); protected ResponseInterceptor.Chain responseInterceptorChain() { ResponseInterceptor.Chain endOfChain = ResponseInterceptor.Chain.DEFAULT; ResponseInterceptor.Chain executionChain = this.responseInterceptors.stream() .reduce(ResponseInterceptor::andThen) .map(interceptor -> interceptor.apply(endOfChain)) .orElse(endOfChain); return (ResponseInterceptor.Chain) Capability.enrich(executionChain, ResponseInterceptor.Chain.class, capabilities); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy