001package ball.http; 002/*- 003 * ########################################################################## 004 * Web API Client (HTTP) Utilities 005 * $Id: ProtocolInvocationHandler.java 6118 2020-06-04 19:31:45Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-http/trunk/src/main/java/ball/http/ProtocolInvocationHandler.java $ 007 * %% 008 * Copyright (C) 2016 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.lang.invoke.MethodHandles; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.InvocationHandler; 026import java.lang.reflect.Method; 027import lombok.ToString; 028import org.apache.http.HttpMessage; 029import org.apache.http.HttpResponse; 030import org.apache.http.client.methods.HttpUriRequest; 031 032import static java.util.Objects.requireNonNull; 033 034/** 035 * Protocol {@link InvocationHandler} for {@link ProtocolClient}. The 036 * {@link #invoke(Object,Method,Object[])} translates the interface 037 * {@link Method} invocation into an {@link HttpMessage} through a 038 * {@link ProtocolRequestBuilder}. If the {@link Method#getReturnType()} is 039 * assignable from the generated {@link HttpMessage}, then it is simply 040 * returned; otherwise, the request is executed through 041 * {@link ProtocolClient#client()} with the 042 * {@link ProtocolClient#context()}. If the {@link Method#getReturnType()} 043 * is {@link HttpResponse} then the response is returned with no further 044 * processing (and the caller is responsible for consuming any entities). 045 * Otherwise, a {@link ProtocolResponseHandler} is provided to the call. 046 * 047 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 048 * @version $Revision: 6118 $ 049 */ 050@ToString 051public class ProtocolInvocationHandler implements InvocationHandler { 052 private final ProtocolClient<?> client; 053 054 /** 055 * Sole constructor. 056 * 057 * @param client The {@link ProtocolClient}. 058 */ 059 protected ProtocolInvocationHandler(ProtocolClient<?> client) { 060 this.client = requireNonNull(client, "client"); 061 } 062 063 @Override 064 public Object invoke(Object proxy, 065 Method method, Object[] argv) throws Throwable { 066 Object result = null; 067 Class<?> declarer = method.getDeclaringClass(); 068 069 if (method.isDefault()) { 070 Constructor<MethodHandles.Lookup> constructor = 071 MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); 072 073 constructor.setAccessible(true); 074 075 result = 076 constructor.newInstance(declarer) 077 .in(declarer) 078 .unreflectSpecial(method, declarer) 079 .bindTo(proxy) 080 .invokeWithArguments(argv); 081 } else if (declarer.equals(Object.class)) { 082 result = method.invoke(proxy, argv); 083 } else { 084 Class<?> returnType = method.getReturnType(); 085 HttpMessage request = 086 new ProtocolRequestBuilder(client).build(method, argv); 087 088 if (returnType.isAssignableFrom(request.getClass())) { 089 result = returnType.cast(request); 090 } else if (returnType.isAssignableFrom(HttpResponse.class)) { 091 result = 092 client.client() 093 .execute((HttpUriRequest) request, client.context()); 094 } else { 095 result = 096 client.client() 097 .execute((HttpUriRequest) request, 098 new ProtocolResponseHandler(client, method), 099 client.context()); 100 } 101 } 102 103 return result; 104 } 105}