001package net.gdface.thrift; 002 003import static com.facebook.swift.codec.metadata.FieldKind.THRIFT_FIELD; 004import static com.google.common.base.Preconditions.*; 005import static java.lang.String.format; 006 007import java.lang.reflect.Constructor; 008import java.lang.reflect.Field; 009import java.lang.reflect.GenericArrayType; 010import java.lang.reflect.InvocationTargetException; 011import java.lang.reflect.Method; 012import java.lang.reflect.ParameterizedType; 013import java.lang.reflect.Type; 014import java.lang.reflect.TypeVariable; 015import java.lang.reflect.WildcardType; 016import java.net.URI; 017import java.net.URL; 018import java.nio.ByteBuffer; 019import java.util.Collection; 020import java.util.Date; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ExecutionException; 026import java.util.concurrent.Executor; 027 028import org.apache.thrift.TApplicationException; 029import org.apache.thrift.protocol.TProtocolException; 030 031import com.facebook.swift.codec.ThriftField.Requiredness; 032import com.facebook.swift.codec.ThriftStruct; 033import com.facebook.swift.codec.metadata.ThriftCatalog; 034import com.facebook.swift.codec.metadata.ThriftCatalogWithTransformer; 035import com.facebook.swift.codec.metadata.ThriftConstructorInjection; 036import com.facebook.swift.codec.metadata.ThriftExtraction; 037import com.facebook.swift.codec.metadata.ThriftFieldExtractor; 038import com.facebook.swift.codec.metadata.ThriftFieldInjection; 039import com.facebook.swift.codec.metadata.ThriftFieldMetadata; 040import com.facebook.swift.codec.metadata.ThriftInjection; 041import com.facebook.swift.codec.metadata.ThriftMethodExtractor; 042import com.facebook.swift.codec.metadata.ThriftMethodInjection; 043import com.facebook.swift.codec.metadata.ThriftParameterInjection; 044import com.facebook.swift.codec.metadata.ThriftStructMetadata; 045import com.facebook.swift.service.RuntimeTApplicationException; 046import com.google.common.base.Throwables; 047import com.google.common.collect.ImmutableMap; 048import com.google.common.collect.ImmutableSet; 049import com.google.common.reflect.TypeParameter; 050import com.google.common.reflect.TypeToken; 051import com.google.common.util.concurrent.FutureCallback; 052import com.google.common.util.concurrent.Futures; 053import com.google.common.util.concurrent.ListenableFuture; 054 055/** 056 * thrift工具 057 * @author guyadong 058 * 059 */ 060public class ThriftUtils { 061 public static final ThriftCatalog CATALOG = new ThriftCatalogWithTransformer(); 062 public static final Set<Class<?>> THRIFT_BUILTIN_KNOWNTYPES = 063 ImmutableSet.of( 064 boolean.class, 065 byte.class, 066 double.class, 067 short.class, 068 int.class, 069 long.class, 070 String.class, 071 ByteBuffer.class, 072 void.class, 073 Boolean.class, 074 Byte.class, 075 Short.class, 076 Integer.class, 077 Long.class, 078 Double.class); 079 public static final Map<Class<?>,Class<?>> CAST_TYPES = 080 ImmutableMap.<Class<?>,Class<?>>builder() 081 .put(byte[].class,ByteBuffer.class) 082 .put(Date.class,Long.class) 083 .put(java.sql.Date.class,Long.class) 084 .put(java.sql.Time.class,Long.class) 085 .put(float.class,double.class) 086 .put(Float.class,Double.class) 087 .put(URI.class,String.class) 088 .put(URL.class,String.class) 089 .build(); 090 public static final String DECORATOR_PKG_SUFFIX="decorator"; 091 public static final String CLIENT_SUFFIX="client"; 092 public static final String DECORATOR_CLIENT_PKG_SUFFIX= DECORATOR_PKG_SUFFIX + "." + CLIENT_SUFFIX; 093 public ThriftUtils() { 094 } 095 096 /** 097 * æž„é€ {@code metadata}æŒ‡å®šç±»åž‹çš„å®žä¾‹å¹¶å¡«å……å—æ®µ 098 * å‚è§ {@link com.facebook.swift.codec.internal.reflection.ReflectionThriftStructCodec#constructStruct(Map<Short, Object>)} 099 * @param data 100 * @param metadata 101 * @return 102 * @throws Exception 103 */ 104 @SuppressWarnings("unchecked") 105 public static <T>T constructStruct(Map<Short, TypeValue> data,ThriftStructMetadata metadata) 106 throws Exception{ 107 T instance; 108 { 109 ThriftConstructorInjection constructor = metadata.getConstructorInjection().get(); 110 Type[] dstTypes = constructor.getConstructor().getGenericParameterTypes(); 111 Type[] srcTypes = new Type[constructor.getParameters().size()]; 112 Object[] parametersValues = new Object[constructor.getParameters().size()]; 113 checkState(dstTypes.length == parametersValues.length); 114 for (ThriftParameterInjection parameter : constructor.getParameters()) { 115 TypeValue value = data.get(parameter.getId()); 116 parametersValues[parameter.getParameterIndex()] = value.value; 117 srcTypes[parameter.getParameterIndex()] = value.type; 118 } 119 for(int i =0;i<dstTypes.length;++i){ 120 parametersValues[i] = TypeTransformer.getInstance().cast(parametersValues[i], srcTypes[i], dstTypes[i]); 121 } 122 try { 123 instance = (T) constructor.getConstructor().newInstance(parametersValues); 124 } catch (InvocationTargetException e) { 125 if (e.getTargetException() != null) { 126 Throwables.throwIfUnchecked(e.getTargetException()); 127 throw new RuntimeException(e.getTargetException()); 128 } 129 throw e; 130 } 131 } 132 return fillStructField(data,metadata,instance); 133 } 134 135 /** 136 * å¡«å……{@code instance}å®žä¾‹çš„å—æ®µ<br> 137 * å‚è§ {@link com.facebook.swift.codec.internal.reflection.ReflectionThriftStructCodec#constructStruct(Map<Short, Object>)} 138 * @param data 139 * @param metadata 140 * @param instance 141 * @return 142 * @throws Exception 143 */ 144 @SuppressWarnings("unchecked") 145 public static <T>T fillStructField(Map<Short, TypeValue> data,ThriftStructMetadata metadata,T instance) 146 throws Exception{ 147 checkArgument(null != instance,"instance is null"); 148 // inject fields 149 for (ThriftFieldMetadata fieldMetadata : metadata.getFields(THRIFT_FIELD)) { 150 for (ThriftInjection injection : fieldMetadata.getInjections()) { 151 if (injection instanceof ThriftFieldInjection) { 152 ThriftFieldInjection fieldInjection = (ThriftFieldInjection) injection; 153 TypeValue value = data.get(fieldInjection.getId()); 154 if (value != null) { 155 Field f = fieldInjection.getField(); 156 f.set(instance, TypeTransformer.getInstance().cast(value.value,value.type,f.getGenericType())); 157 } 158 } 159 } 160 } 161 162 // inject methods 163 for (ThriftMethodInjection methodInjection : metadata.getMethodInjections()) { 164 boolean shouldInvoke = false; 165 Object[] parametersValues = new Object[methodInjection.getParameters().size()]; 166 Type[] srcTypes = new Type[methodInjection.getParameters().size()]; 167 for (ThriftParameterInjection parameter : methodInjection.getParameters()) { 168 TypeValue value = data.get(parameter.getId()); 169 if (value != null) { 170 parametersValues[parameter.getParameterIndex()] = value.value; 171 srcTypes[parameter.getParameterIndex()] = value.type; 172 shouldInvoke = true; 173 } 174 } 175 176 if (shouldInvoke) { 177 try { 178 Method method = methodInjection.getMethod(); 179 Type[] parameterTypes = method.getGenericParameterTypes(); 180 for(int i = 0 ;i<parametersValues.length;++i){ 181 parametersValues[i]=TypeTransformer.getInstance().cast(parametersValues[i], srcTypes[i], parameterTypes[i]); 182 } 183 method.invoke(instance, parametersValues); 184 } 185 catch (InvocationTargetException e) { 186 if (e.getTargetException() != null) { 187 Throwables.throwIfUnchecked(e.getTargetException()); 188 throw new RuntimeException(e.getTargetException()); 189 } 190 throw e; 191 } 192 } 193 } 194 195 // builder method 196 if (metadata.getBuilderMethod().isPresent()) { 197 ThriftMethodInjection builderMethod = metadata.getBuilderMethod().get(); 198 Object[] parametersValues = new Object[builderMethod.getParameters().size()]; 199 for (ThriftParameterInjection parameter : builderMethod.getParameters()) { 200 Object value = data.get(parameter.getId()); 201 parametersValues[parameter.getParameterIndex()] = value; 202 } 203 204 try { 205 instance = (T) builderMethod.getMethod().invoke(instance, parametersValues); 206 if (instance == null) { 207 throw new IllegalArgumentException("Builder method returned a null instance"); 208 209 } 210 if (!metadata.getStructClass().isInstance(instance)) { 211 throw new IllegalArgumentException(format("Builder method returned instance of type %s, but an instance of %s is required", 212 instance.getClass().getName(), 213 metadata.getStructClass().getName())); 214 } 215 } 216 catch (InvocationTargetException e) { 217 if (e.getTargetException() != null) { 218 Throwables.throwIfUnchecked(e.getTargetException()); 219 throw new RuntimeException(e.getTargetException()); 220 } 221 throw e; 222 } 223 } 224 return (T) instance; 225 } 226 227 /** 228 * 获å–{@code field}æŒ‡å®šçš„å—æ®µå€¼<br> 229 * 230 * @param instance 231 * @param field 232 * @return 233 * @throws Exception 234 * @see {@link com.facebook.swift.codec.internal.reflection.AbstractReflectionThriftCodec#getFieldValue(Object, ThriftFieldMetadata)} 235 */ 236 public static TypeValue getFieldValue(Object instance, ThriftFieldMetadata field) throws Exception { 237 try { 238 if (field.getExtraction().isPresent()) { 239 ThriftExtraction extraction = field.getExtraction().get(); 240 if (extraction instanceof ThriftFieldExtractor) { 241 ThriftFieldExtractor thriftFieldExtractor = (ThriftFieldExtractor) extraction; 242 Field f = thriftFieldExtractor.getField(); 243 return new TypeValue(f.getGenericType(),f.get(instance)); 244 } else if (extraction instanceof ThriftMethodExtractor) { 245 ThriftMethodExtractor thriftMethodExtractor = (ThriftMethodExtractor) extraction; 246 Method method = thriftMethodExtractor.getMethod(); 247 return new TypeValue(method.getGenericReturnType(),method.invoke(instance)); 248 } 249 throw new IllegalAccessException("Unsupported field extractor type " + extraction.getClass().getName()); 250 } 251 throw new IllegalAccessException("No extraction present for " + field); 252 } catch (InvocationTargetException e) { 253 if (e.getTargetException() != null) { 254 Throwables.throwIfInstanceOf(e.getTargetException(), Exception.class); 255 } 256 throw e; 257 } 258 } 259 260 /** 261 * æ ¹æ®{@code metadata}类型数æ®èŽ·å–{@code instance}å®žä¾‹æ‰€æœ‰çš„å—æ®µå€¼ 262 * å‚è§ {@link com.facebook.swift.codec.internal.reflection.ReflectionThriftStructCodec#write(Object, org.apache.thrift.protocol.TProtocol)} 263 * @param instance 264 * @param metadata 265 * @return å—æ®µå€¼æ˜ 射表 266 */ 267 public static Map<Short, TypeValue> getFieldValues(Object instance, ThriftStructMetadata metadata) { 268 checkArgument(null != instance && null != metadata && metadata.getStructClass().isInstance(instance), 269 "instance,metadata must not be null"); 270 271 Collection<ThriftFieldMetadata> fields = metadata.getFields(THRIFT_FIELD); 272 Map<Short, TypeValue> data = new HashMap<>(fields.size()); 273 for (ThriftFieldMetadata field : fields) { 274 try { 275 // is the field readable? 276 if (field.isWriteOnly()) { 277 continue; 278 } 279 TypeValue value = getFieldValue(instance, field); 280 if (value.value == null) { 281 if (field.getRequiredness() == Requiredness.REQUIRED) { 282 throw new TProtocolException("required field was not set"); 283 } else { 284 continue; 285 } 286 } 287 data.put(field.getId(), value); 288 } catch (Exception e) { 289 Throwables.throwIfUnchecked(e); 290 throw new RuntimeException(e); 291 } 292 } 293 return data; 294 } 295 /** 296 * @param instance 297 * @param metadata 298 * @return 299 * @deprecated name spell error, replaced by {@link #getFieldValues(Object, ThriftStructMetadata)} 300 */ 301 public static Map<Short, TypeValue> getFiledValues(Object instance, ThriftStructMetadata metadata) { 302 return getFieldValues(instance,metadata); 303 } 304 public static boolean isThriftStruct(Type type){ 305 return type instanceof Class<?> 306 ? ((Class<?>)type).isAnnotationPresent(ThriftStruct.class) 307 : false; 308 } 309 310 public static boolean isThriftDecorator(Type type){ 311 return type instanceof Class<?> 312 ? ThriftDecorator.class.isAssignableFrom((Class<?>)type) 313 : false; 314 } 315 316 public static boolean isPrimitiveArray(Type type){ 317 if(type instanceof Class<?>){ 318 Class<?> clazz = (Class<?>)type; 319 return clazz.isArray() && clazz.getComponentType().isPrimitive(); 320 } 321 return false; 322 } 323 324 public static boolean isThriftBuildinType(Type type){ 325 return THRIFT_BUILTIN_KNOWNTYPES.contains(type); 326 } 327 328 public static boolean isPrimitivefloat(Type type){ 329 return type == float.class; 330 } 331 public static boolean isfloat(Type type){ 332 return type == float.class || type == Float.class; 333 } 334 public static boolean isCastType(Type type){ 335 return CAST_TYPES.containsKey(type); 336 } 337 338 public static boolean isException(Type type){ 339 return null == type 340 ? false 341 : Exception.class.isAssignableFrom(TypeToken.of(type).getRawType()); 342 } 343 public static <T>Constructor<T> getConstructor(Class<T> clazz,Class<?>...parameterTypes){ 344 try { 345 return clazz.getConstructor(parameterTypes); 346 } catch (NoSuchMethodException e) { 347 return null; 348 } 349 } 350 public static <T> boolean hasConstructor(Class<T> clazz,Class<?>...parameterTypes){ 351 return getConstructor(clazz,parameterTypes) != null; 352 } 353 public static boolean isThriftException(Type type){ 354 return isException(type) && isThriftStruct(type); 355 } 356 public static boolean isThriftException(Type left, Type right){ 357 return isThriftException(left) && isThriftException(right); 358 } 359 360 public static boolean needTransformer(Type type){ 361 return ! isThriftBuildinType(type) && ! isfloat(type); 362 } 363 public static interface Action{ 364 void doClass(Class<?> type); 365 } 366 public static void traverseTypes(Type type,Action action){ 367 checkArgument(null !=action,"action is null"); 368 if(type instanceof Class<?>){ 369 action.doClass((Class<?>) type); 370 }else if( type instanceof ParameterizedType){ 371 ParameterizedType paramType = (ParameterizedType)type; 372 Type rawType = paramType.getRawType(); 373 Type[] typeArgs = paramType.getActualTypeArguments(); 374 traverseTypes(rawType,action); 375 for(Type arg:typeArgs){ 376 traverseTypes(arg,action); 377 } 378 }else if (type instanceof GenericArrayType) { 379 traverseTypes(((GenericArrayType) type).getGenericComponentType(),action); 380 } else if (type instanceof TypeVariable) { 381 for (Type t : ((TypeVariable<?>) type).getBounds()) { 382 traverseTypes(t,action); 383 } 384 } else if (type instanceof WildcardType) { 385 for (Type t : ((WildcardType) type).getLowerBounds()) { 386 traverseTypes(t,action); 387 } 388 for (Type t : ((WildcardType) type).getUpperBounds()) { 389 traverseTypes(t,action); 390 } 391 } else{ 392 throw new IllegalArgumentException(String.format("not allow type %s", type.toString())); 393 } 394 } 395 @SuppressWarnings("serial") 396 public static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken) { 397 return new TypeToken<Map<K, V>>() {} 398 .where(new TypeParameter<K>() {}, keyToken) 399 .where(new TypeParameter<V>() {}, valueToken); 400 } 401 @SuppressWarnings("serial") 402 public static <T> TypeToken<List<T>> listToken(TypeToken<T> keyToken) { 403 return new TypeToken<List<T>>() {} 404 .where(new TypeParameter<T>() {}, keyToken); 405 } 406 @SuppressWarnings("serial") 407 public static <T> TypeToken<Set<T>> setToken(TypeToken<T> keyToken) { 408 return new TypeToken<Set<T>>() {} 409 .where(new TypeParameter<T>() {}, keyToken); 410 } 411 412 /** 413 * @param type 414 * @return 415 * @see #getDecoratorType(Type) 416 */ 417 public static boolean hasDecoratorType(Type type){ 418 return getDecoratorType(type) !=null; 419 } 420 /** 421 * @param type 422 * @return 423 * @see #getDecoratorType(Class) 424 */ 425 @SuppressWarnings("unchecked") 426 public static <T>Class<? extends ThriftDecorator<T>> getDecoratorType(Type type){ 427 if(!isThriftStruct(type)){ 428 return getDecoratorType((Class<T>)type); 429 } 430 return null; 431 } 432 /** 433 * 返回{@code clazz}对应的装饰类 434 * @param clazz 435 * @return 如果没有装饰类则返回{@code null} 436 */ 437 @SuppressWarnings("unchecked") 438 public static <T,D extends ThriftDecorator<T>>Class<D> getDecoratorType(Class<T> clazz){ 439 if(!isThriftStruct(clazz)){ 440 String decoratorClazzName = clazz.getPackage().getName() 441 + "." 442 + DECORATOR_CLIENT_PKG_SUFFIX 443 + "." 444 + clazz.getSimpleName(); 445 try { 446 Class<?> decoratorClazz = Class.forName(decoratorClazzName); 447 checkState(isThriftDecoratorPair(decoratorClazz,clazz), 448 "%s must immplement %s", 449 decoratorClazz.getName(), 450 ThriftDecorator.class.getName()); 451 return (Class<D>) decoratorClazz; 452 } catch (ClassNotFoundException e) { 453 } 454 } 455 return null; 456 } 457 458 /** 459 * åˆ¤æ– {@code left and right}之间是å¦ä¸ºè£…饰类和被装饰类关系 460 * @param left 装饰类 461 * @param right 补装饰类 462 * @return 463 */ 464 public static <L,R>boolean isThriftDecoratorPair(Class<L>left,Class<R>right){ 465 try { 466 return isThriftDecorator(left) 467 && left.getMethod("delegate").getReturnType() == right; 468 } catch (NoSuchMethodException e) { 469 throw new RuntimeException(e); 470 } 471 } 472 473 /** 474 * åˆ¤æ– {@code right}是å¦ä¸º{@code left}对应的clientç«¯å˜æ ¹ç±»åž‹ 475 * @param left 476 * @param right 477 * @return 478 */ 479 public static <L,R>boolean isThriftClientPair(Class<L>left,Class<R>right){ 480 return getMiddleClass(left,right)!=null; 481 } 482 /** 483 * 返回 {@code left & right}之间的decorator类型 484 * @param left 原始类型 485 * @param right {@code left}对应的clientç«¯å˜æ ¹ç±»åž‹ 486 * @return 487 */ 488 public static <L,M extends ThriftDecorator<L>,R>Class<M> getMiddleClass(Class<L>left,Class<R>right){ 489 if(isThriftStruct(right)){ 490 Class<M> decoratorClass = getDecoratorType(left); 491 if(null != decoratorClass && decoratorClass.getSimpleName().equals(left.getSimpleName())){ 492 return decoratorClass; 493 } 494 } 495 return null; 496 } 497 /** 498 * @param left 499 * @param right 500 * @return 501 * @see #getMiddleClass(Class, Class) 502 */ 503 @SuppressWarnings("unchecked") 504 public static <L,M extends ThriftDecorator<L>,R>Class<M> getMiddleClassChecked(Class<L>left,Class<R>right){ 505 return (Class<M>) checkNotNull( 506 getMiddleClass(left,right), 507 "NOT FOUND decorator class for %s", 508 left.getName()); 509 } 510 public static final String ISLOCAL_METHOD_NAME = "isLocal"; 511 public static boolean isIsLocalMethod(Method method){ 512 if(null == method){ 513 return false; 514 } 515 return method.getName().equals(ISLOCAL_METHOD_NAME) 516 && method.getParameterTypes().length == 0 517 && method.getExceptionTypes().length == 0 518 && method.getReturnType() == boolean.class; 519 } 520 521 /** é¿å…{@code null}抛出异常 */ 522 public static <T> T returnNull(RuntimeTApplicationException e){ 523 Throwable cause = e.getCause(); 524 if (cause instanceof TApplicationException 525 && ((TApplicationException) cause).getType() == TApplicationException.MISSING_RESULT){ 526 return null; 527 } 528 throw e; 529 } 530 /** é¿å…{@code null}抛出异常 531 * @throws Throwable */ 532 public static <T> T returnNull(Throwable e) throws Throwable{ 533 if(e instanceof RuntimeTApplicationException){ 534 return returnNull((RuntimeTApplicationException)e); 535 } 536 throw e; 537 } 538 public static<V> void addCallback( 539 final ListenableFuture<V> future, 540 final FutureCallback<? super V> callback,Executor executor) { 541 checkArgument(null != callback,"callback is null"); 542 checkArgument(null != executor,"executor is null"); 543 Runnable callbackListener = 544 new Runnable() { 545 @Override 546 public void run() { 547 V value; 548 try { 549 value = Futures.getDone(future); 550 } catch (ExecutionException e) { 551 try{ 552 // value is null 553 value = returnNull(e.getCause()); 554 }catch(Throwable t){ 555 callback.onFailure(t); 556 return; 557 } 558 } catch (Throwable e) { 559 callback.onFailure(e); 560 return; 561 } 562 callback.onSuccess(value); 563 } 564 }; 565 future.addListener(callbackListener, executor); 566 } 567}