001package ball.http; 002/*- 003 * ########################################################################## 004 * Web API Client (HTTP) Utilities 005 * $Id: ProtocolResponseHandler.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/ProtocolResponseHandler.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 com.fasterxml.jackson.databind.JavaType; 024import com.fasterxml.jackson.databind.ObjectMapper; 025import com.fasterxml.jackson.databind.type.TypeFactory; 026import java.io.IOException; 027import java.io.InputStream; 028import java.lang.reflect.Array; 029import java.lang.reflect.GenericArrayType; 030import java.lang.reflect.Method; 031import java.lang.reflect.ParameterizedType; 032import java.lang.reflect.Type; 033/* import java.lang.reflect.TypeVariable; */ 034/* import java.lang.reflect.WildcardType; */ 035import java.util.Collection; 036import java.util.Map; 037import javax.xml.bind.JAXBException; 038import lombok.ToString; 039import org.apache.http.HttpEntity; 040import org.apache.http.client.ClientProtocolException; 041import org.apache.http.entity.ContentType; 042import org.apache.http.impl.client.AbstractResponseHandler; 043import org.apache.http.util.EntityUtils; 044 045import static java.util.Objects.requireNonNull; 046 047/** 048 * {@link ProtocolClient} {@link org.apache.http.client.ResponseHandler} 049 * implementation. Makes use of {@link ProtocolClient#getUnmarshaller()} 050 * and {@link ProtocolClient#getObjectMapper()} for de-serialization. 051 * 052 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 053 * @version $Revision: 6118 $ 054 */ 055@ToString 056public class ProtocolResponseHandler extends AbstractResponseHandler<Object> { 057 private final ProtocolClient<?> client; 058 private final Method method; 059 060 /** 061 * Sole constructor. 062 * 063 * @param client The {@link ProtocolClient}. 064 * @param method The protocol {@link Method}. 065 */ 066 protected ProtocolResponseHandler(ProtocolClient<?> client, 067 Method method) { 068 super(); 069 070 this.client = requireNonNull(client, "client"); 071 this.method = requireNonNull(method, "method"); 072 } 073 074 @Override 075 public Object handleEntity(HttpEntity entity) throws ClientProtocolException, 076 IOException { 077 Object object = null; 078 079 try { 080 String type = 081 ContentType.getLenientOrDefault(entity).getMimeType() 082 .replaceAll("[^\\p{Alnum}]", "_").toUpperCase(); 083 084 object = 085 getClass() 086 .getDeclaredMethod(type, HttpEntity.class) 087 .invoke(this, entity); 088 } catch (NoSuchMethodException exception) { 089 object = EntityUtils.toString(entity); 090 } catch (Exception exception) { 091 if (exception instanceof ClientProtocolException) { 092 throw (ClientProtocolException) exception; 093 } else if (exception instanceof IOException) { 094 throw (IOException) exception; 095 } else { 096 throw new ClientProtocolException(exception); 097 } 098 } 099 100 return method.getReturnType().cast(object); 101 } 102 103 protected Object APPLICATION_JSON(HttpEntity entity) throws ClientProtocolException, 104 IOException { 105 Object object = null; 106 ObjectMapper om = client.getObjectMapper(); 107 TypeFactory factory = om.getTypeFactory(); 108 JavaType type = 109 getJavaTypeFrom(factory, method.getGenericReturnType()); 110 111 try (InputStream in = entity.getContent()) { 112 object = om.readValue(in, type); 113 } 114 115 return object; 116 } 117 118 private JavaType getJavaTypeFrom(TypeFactory factory, Type type) { 119 JavaType java = null; 120 121 if (type instanceof GenericArrayType) { 122 java = getJavaTypeFrom(factory, (GenericArrayType) type); 123 } else if (type instanceof ParameterizedType) { 124 java = getJavaTypeFrom(factory, (ParameterizedType) type); 125/* 126 } else if (type instanceof TypeVariable) { 127 } else if (type instanceof WildcardType) { 128*/ 129 } 130 131 return (java != null) ? java : factory.constructType(type); 132 } 133 134 private JavaType getJavaTypeFrom(TypeFactory factory, 135 GenericArrayType type) { 136 Type element = type.getGenericComponentType(); 137 138 return factory.constructArrayType(getJavaTypeFrom(factory, element)); 139 } 140 141 private JavaType getJavaTypeFrom(TypeFactory factory, 142 ParameterizedType type) { 143 JavaType java = null; 144 Class<?> raw = (Class<?>) type.getRawType(); 145 Type[] arguments = type.getActualTypeArguments(); 146 147 if (Collection.class.isAssignableFrom(raw)) { 148 java = 149 factory 150 .constructCollectionType(raw.asSubclass(Collection.class), 151 getJavaTypeFrom(factory, arguments[0])); 152 } else if (Map.class.isAssignableFrom(raw)) { 153 java = 154 factory 155 .constructMapType(raw.asSubclass(Map.class), 156 getJavaTypeFrom(factory, arguments[0]), 157 getJavaTypeFrom(factory, arguments[1])); 158 } 159 160 return (java != null) ? java : factory.constructType(type); 161 } 162 163 protected Object APPLICATION_XML(HttpEntity entity) throws ClientProtocolException, 164 IOException { 165 Object object = null; 166 167 try (InputStream in = entity.getContent()) { 168 object = client.getUnmarshaller().unmarshal(in); 169 } catch (JAXBException exception) { 170 throw new IOException(exception); 171 } 172 173 return object; 174 } 175}