001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.ws.client;
015
016import java.lang.reflect.InvocationHandler;
017import java.lang.reflect.Method;
018import java.lang.reflect.Proxy;
019import java.util.Map;
020import java.util.Optional;
021
022import feign.InvocationHandlerFactory;
023import feign.Target;
024
025import static feign.Util.checkNotNull;
026
027@SuppressWarnings("unused")
028public class ClientInvocationHandlerFactory implements InvocationHandlerFactory {
029
030  @Override
031  public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
032    return new ClientInvocationHandlerFactory.FeignInvocationHandler(target, dispatch);
033  }
034
035  static class FeignInvocationHandler implements InvocationHandler {
036
037    private final Target target;
038    private final Map<Method, MethodHandler> dispatch;
039
040    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
041      this.target = checkNotNull(target, "target");
042      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
043    }
044
045    @Override
046    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
047      if ("equals".equals(method.getName())) {
048        try {
049          Object otherHandler =
050              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
051          return equals(otherHandler);
052        } catch (IllegalArgumentException e) {
053          return false;
054        }
055      } else if ("hashCode".equals(method.getName())) {
056        return hashCode();
057      } else if ("toString".equals(method.getName())) {
058        return toString();
059      }
060
061      return getMethodHandler(method).invoke(args);
062    }
063
064    private MethodHandler getMethodHandler(Method method) {
065      MethodHandler methodHandler = dispatch.get(method);
066
067      // if the result is not null just return the handler
068      if (methodHandler != null) {
069        return methodHandler;
070      }
071
072      // if not we have to find another handler
073      Optional<Method> anotherMethod =
074          dispatch.keySet().stream()
075              .filter(methodFromDispatch -> !methodFromDispatch.equals(method))
076              .filter(methodFromDispatch -> methodFromDispatch.getName().equals(method.getName()))
077              .filter(
078                  methodFromDispatch ->
079                      methodFromDispatch.getParameterCount() == method.getParameterCount())
080              .findFirst();
081
082      return anotherMethod.map(dispatch::get).orElse(null);
083    }
084
085    @Override
086    public boolean equals(Object obj) {
087      if (obj instanceof ClientInvocationHandlerFactory.FeignInvocationHandler) {
088        ClientInvocationHandlerFactory.FeignInvocationHandler other =
089            (ClientInvocationHandlerFactory.FeignInvocationHandler) obj;
090        return target.equals(other.target);
091      }
092      return false;
093    }
094
095    @Override
096    public int hashCode() {
097      return target.hashCode();
098    }
099
100    @Override
101    public String toString() {
102      return target.toString();
103    }
104  }
105}