@Override public ServiceSpecification generateSpecification(Set<ServiceConfig> serviceConfigs) { final Map<Class<?>, EntryBuilder> map = new LinkedHashMap<>(); for (ServiceConfig c : serviceConfigs) { final THttpService service = c.service().as(THttpService.class).get(); service.entries().forEach((serviceName, entry) -> { for (Class<?> iface : entry.interfaces()) { final Class<?> serviceClass = iface.getEnclosingClass(); final EntryBuilder builder = map.computeIfAbsent(serviceClass, cls -> new EntryBuilder(serviceClass)); // Add all available endpoints. Accept only the services with exact and prefix path // mappings, whose endpoint path can be determined. final PathMapping pathMapping = c.pathMapping(); final String path = pathMapping.exactPath().orElse(pathMapping.prefix().orElse(null)); if (path != null) { builder.endpoint(new EndpointInfoBuilder(c.virtualHost().hostnamePattern(), path) .fragment(serviceName) .defaultFormat(service.defaultSerializationFormat()) .availableFormats(service.allowedSerializationFormats()) .build()); } } }); } final List<Entry> entries = map.values().stream() .map(EntryBuilder::build) .collect(Collectors.toList()); return generate(entries); }
private static NamedTypeInfo newNamedTypeInfo(TypeSignature typeSignature) { final Optional<Object> namedTypeDescriptor = typeSignature.namedTypeDescriptor(); if (!namedTypeDescriptor.isPresent()) { throw new IllegalArgumentException("cannot create a named type from: " + typeSignature); } final Class<?> type = (Class<?>) namedTypeDescriptor.get(); if (type.isEnum()) { @SuppressWarnings("unchecked") final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) type; return new EnumInfo(enumType); } if (TException.class.isAssignableFrom(type)) { @SuppressWarnings("unchecked") final Class<? extends TException> castType = (Class<? extends TException>) type; return newExceptionInfo(castType); } assert TBase.class.isAssignableFrom(type); @SuppressWarnings("unchecked") final Class<? extends TBase<?, ?>> castType = (Class<? extends TBase<?, ?>>) type; return newStructInfo(castType); }
@VisibleForTesting static StructInfo newStructInfo(Class<? extends TBase<?, ?>> structClass) { final String name = structClass.getName(); final Map<?, FieldMetaData> metaDataMap = FieldMetaData.getStructMetaDataMap(structClass); final List<FieldInfo> fields = metaDataMap.values().stream() .map(fieldMetaData -> newFieldInfo(structClass, fieldMetaData)) .collect(Collectors.toList()); return new StructInfo(name, fields); }
@VisibleForTesting static FieldInfo newFieldInfo(Class<?> parentType, FieldMetaData fieldMetaData) { requireNonNull(fieldMetaData, "fieldMetaData"); final FieldValueMetaData fieldValueMetaData = fieldMetaData.valueMetaData; final TypeSignature typeSignature; if (fieldValueMetaData.isStruct() && fieldValueMetaData.isTypedef() && parentType.getSimpleName().equals(fieldValueMetaData.getTypedefName())) { // Handle the special case where a struct field refers to itself, // where the Thrift compiler handles it as a typedef. typeSignature = TypeSignature.ofNamed(parentType); } else { typeSignature = toTypeSignature(fieldValueMetaData); } return new FieldInfo(fieldMetaData.fieldName, convertRequirement(fieldMetaData.requirementType), typeSignature); }
@Test public void testNewExceptionInfo() { final ExceptionInfo exception = newExceptionInfo(FooServiceException.class); assertThat(exception).isEqualTo(new ExceptionInfo( FooServiceException.class.getName(), ImmutableList.of(newFieldInfo( FooServiceException.class, new FieldMetaData("stringVal", TFieldRequirementType.DEFAULT, new FieldValueMetaData(TType.STRING, false)))))); }
@Override public Optional<String> guessServiceName(Object exampleRequest) { final TBase<?, ?> exampleTBase = asTBase(exampleRequest); if (exampleTBase == null) { return Optional.empty(); } return Optional.of(exampleTBase.getClass().getEnclosingClass().getName()); }
@VisibleForTesting static ServiceInfo newServiceInfo(Class<?> serviceClass, Iterable<EndpointInfo> endpoints) { requireNonNull(serviceClass, "serviceClass"); final String name = serviceClass.getName(); final ClassLoader serviceClassLoader = serviceClass.getClassLoader(); final String interfaceClassName = name + "$Iface"; final Class<?> interfaceClass; try { interfaceClass = Class.forName(interfaceClassName, false, serviceClassLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException("failed to find a class: " + interfaceClassName, e); } final Method[] methods = interfaceClass.getDeclaredMethods(); return new ServiceInfo(name, Arrays.stream(methods).map(m -> newMethodInfo(m, endpoints))::iterator); }
@VisibleForTesting static ServiceSpecification generate(List<Entry> entries) { final List<ServiceInfo> services = entries.stream() .map(e -> newServiceInfo(e.serviceType, e.endpointInfos)) .collect(toImmutableList()); return ServiceSpecification.generate(services, ThriftDocServicePlugin::newNamedTypeInfo); }
return TypeSignature.ofList(toTypeSignature(((ListMetaData) fieldValueMetaData).elemMetaData)); return TypeSignature.ofSet(toTypeSignature(((SetMetaData) fieldValueMetaData).elemMetaData)); return TypeSignature.ofMap(toTypeSignature(((MapMetaData) fieldValueMetaData).keyMetaData), toTypeSignature(((MapMetaData) fieldValueMetaData).valueMetaData));
private static NamedTypeInfo newNamedTypeInfo(TypeSignature typeSignature) { final Class<?> type = (Class<?>) typeSignature.namedTypeDescriptor().get(); if (type.isEnum()) { return newEnumInfo(type); } if (TException.class.isAssignableFrom(type)) { @SuppressWarnings("unchecked") final Class<? extends TException> castType = (Class<? extends TException>) type; return newExceptionInfo(castType); } assert TBase.class.isAssignableFrom(type); @SuppressWarnings("unchecked") final Class<? extends TBase<?, ?>> castType = (Class<? extends TBase<?, ?>>) type; return newStructInfo(castType); }
@Test public void testNewStructInfoTest() throws Exception { final TypeSignature string = TypeSignature.ofBase("string"); final List<FieldInfo> fields = new ArrayList<>(); fields.add(new FieldInfo("boolVal", FieldRequirement.DEFAULT, TypeSignature.ofBase("bool"))); fields.add(new FieldInfo("byteVal", FieldRequirement.DEFAULT, TypeSignature.ofBase("i8"))); fields.add(new FieldInfo("i16Val", FieldRequirement.DEFAULT, TypeSignature.ofBase("i16"))); fields.add(new FieldInfo("i32Val", FieldRequirement.DEFAULT, TypeSignature.ofBase("i32"))); fields.add(new FieldInfo("i64Val", FieldRequirement.DEFAULT, TypeSignature.ofBase("i64"))); fields.add(new FieldInfo("doubleVal", FieldRequirement.DEFAULT, TypeSignature.ofBase("double"))); fields.add(new FieldInfo("stringVal", FieldRequirement.DEFAULT, string)); fields.add(new FieldInfo("binaryVal", FieldRequirement.DEFAULT, TypeSignature.ofBase("binary"))); fields.add(new FieldInfo("enumVal", FieldRequirement.DEFAULT, TypeSignature.ofNamed(FooEnum.class))); fields.add(new FieldInfo("unionVal", FieldRequirement.DEFAULT, TypeSignature.ofNamed(FooUnion.class))); fields.add(new FieldInfo("mapVal", FieldRequirement.DEFAULT, TypeSignature.ofMap(string, TypeSignature.ofNamed(FooEnum.class)))); fields.add(new FieldInfo("setVal", FieldRequirement.DEFAULT, TypeSignature.ofSet(FooUnion.class))); fields.add(new FieldInfo("listVal", FieldRequirement.DEFAULT, TypeSignature.ofList(string))); fields.add(new FieldInfo("selfRef", FieldRequirement.OPTIONAL, TypeSignature.ofNamed(FooStruct.class))); final StructInfo fooStruct = newStructInfo(FooStruct.class); assertThat(fooStruct).isEqualTo(new StructInfo(FooStruct.class.getName(), fields)); }
@VisibleForTesting static FieldInfo newFieldInfo(Class<?> parentType, FieldMetaData fieldMetaData) { requireNonNull(fieldMetaData, "fieldMetaData"); final FieldValueMetaData fieldValueMetaData = fieldMetaData.valueMetaData; final TypeSignature typeSignature; if (fieldValueMetaData.isStruct() && fieldValueMetaData.isTypedef() && parentType.getSimpleName().equals(fieldValueMetaData.getTypedefName())) { // Handle the special case where a struct field refers to itself, // where the Thrift compiler handles it as a typedef. typeSignature = TypeSignature.ofNamed(parentType); } else { typeSignature = toTypeSignature(fieldValueMetaData); } return new FieldInfo(fieldMetaData.fieldName, convertRequirement(fieldMetaData.requirementType), typeSignature); }
@Override public Optional<String> guessServiceMethodName(Object exampleRequest) { final TBase<?, ?> exampleTBase = asTBase(exampleRequest); if (exampleTBase == null) { return Optional.empty(); } final String typeName = exampleTBase.getClass().getName(); return Optional.of(typeName.substring(typeName.lastIndexOf('$') + 1, typeName.length() - REQUEST_STRUCT_SUFFIX.length())); }
newMethodInfo(methodName, argsClass, (Class<? extends TBase<?, ?>>) resultClass, (Class<? extends TException>[]) method.getExceptionTypes(),
@Test public void testNewServiceInfo() { final ServiceInfo service = newServiceInfo(FooService.class, ImmutableList.of( new EndpointInfoBuilder("*", "/foo") .fragment("a")
@Test public void incompleteStructMetadata() throws Exception { assertThat(toTypeSignature(new FieldValueMetaData(TType.STRUCT))) .isEqualTo(TypeSignature.ofUnresolved("unknown")); } }
@VisibleForTesting static FieldInfo newFieldInfo(Class<?> parentType, FieldMetaData fieldMetaData) { requireNonNull(fieldMetaData, "fieldMetaData"); final FieldValueMetaData fieldValueMetaData = fieldMetaData.valueMetaData; final TypeSignature typeSignature; if (fieldValueMetaData.isStruct() && fieldValueMetaData.isTypedef() && parentType.getSimpleName().equals(fieldValueMetaData.getTypedefName())) { // Handle the special case where a struct field refers to itself, // where the Thrift compiler handles it as a typedef. typeSignature = TypeSignature.ofNamed(parentType); } else { typeSignature = toTypeSignature(fieldValueMetaData); } return new FieldInfo(fieldMetaData.fieldName, convertRequirement(fieldMetaData.requirementType), typeSignature); }
@VisibleForTesting static ExceptionInfo newExceptionInfo(Class<? extends TException> exceptionClass) { requireNonNull(exceptionClass, "exceptionClass"); final String name = exceptionClass.getName(); List<FieldInfo> fields; try { @SuppressWarnings("unchecked") final Map<?, FieldMetaData> metaDataMap = (Map<?, FieldMetaData>) exceptionClass.getDeclaredField("metaDataMap").get(null); fields = metaDataMap.values().stream() .map(fieldMetaData -> newFieldInfo(exceptionClass, fieldMetaData)) .collect(toImmutableList()); } catch (IllegalAccessException e) { throw new AssertionError("will not happen", e); } catch (NoSuchFieldException ignored) { fields = Collections.emptyList(); } return new ExceptionInfo(name, fields); }
private static NamedTypeInfo newNamedTypeInfo(TypeSignature typeSignature) { final Optional<Object> namedTypeDescriptor = typeSignature.namedTypeDescriptor(); if (!namedTypeDescriptor.isPresent()) { throw new IllegalArgumentException("cannot create a named type from: " + typeSignature); } final Class<?> type = (Class<?>) namedTypeDescriptor.get(); if (type.isEnum()) { @SuppressWarnings("unchecked") final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) type; return new EnumInfo(enumType); } if (TException.class.isAssignableFrom(type)) { @SuppressWarnings("unchecked") final Class<? extends TException> castType = (Class<? extends TException>) type; return newExceptionInfo(castType); } assert TBase.class.isAssignableFrom(type); @SuppressWarnings("unchecked") final Class<? extends TBase<?, ?>> castType = (Class<? extends TBase<?, ?>>) type; return newStructInfo(castType); }
@Override public Optional<String> guessServiceName(Object exampleRequest) { final TBase<?, ?> exampleTBase = asTBase(exampleRequest); if (exampleTBase == null) { return Optional.empty(); } return Optional.of(exampleTBase.getClass().getEnclosingClass().getName()); }