Skip to content

Commit

Permalink
Make the FieldQueryMapEncoder encoder thread safe (#1369)
Browse files Browse the repository at this point in the history
* Make the FieldQueryMapEncoder encoder thread safe
Change the caching map to be concurrent and only
insert items that are missing to the map.
Fixes #1257

* Refactor the FieldQueryMapEncoder to use streams
Fixes #1257
  • Loading branch information
Budlee committed Apr 22, 2021
1 parent 3ea3708 commit b53a535
Showing 1 changed file with 44 additions and 25 deletions.
69 changes: 44 additions & 25 deletions core/src/main/java/feign/querymap/FieldQueryMapEncoder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2012-2020 The Feign Authors
* Copyright 2012-2021 The Feign Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand All @@ -13,12 +13,17 @@
*/
package feign.querymap;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import feign.Param;
import feign.QueryMapEncoder;
import feign.codec.EncodeException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
* the query map will be generated using member variable names as query parameter names.
Expand All @@ -31,36 +36,34 @@
public class FieldQueryMapEncoder implements QueryMapEncoder {

private final Map<Class<?>, ObjectParamMetadata> classToMetadata =
new HashMap<Class<?>, ObjectParamMetadata>();
new ConcurrentHashMap<>();

@Override
public Map<String, Object> encode(Object object) throws EncodeException {
ObjectParamMetadata metadata =
classToMetadata.computeIfAbsent(object.getClass(), ObjectParamMetadata::parseObjectType);

return metadata.objectFields.stream()
.map(field -> this.FieldValuePair(object, field))
.filter(fieldObjectPair -> fieldObjectPair.right.isPresent())
.collect(Collectors.toMap(this::fieldName,
fieldObjectPair -> fieldObjectPair.right.get()));

}

private String fieldName(Pair<Field, Optional<Object>> pair) {
Param alias = pair.left.getAnnotation(Param.class);
return alias != null ? alias.value() : pair.left.getName();
}

private Pair<Field, Optional<Object>> FieldValuePair(Object object, Field field) {
try {
ObjectParamMetadata metadata = getMetadata(object.getClass());
Map<String, Object> fieldNameToValue = new HashMap<String, Object>();
for (Field field : metadata.objectFields) {
Object value = field.get(object);
if (value != null && value != object) {
Param alias = field.getAnnotation(Param.class);
String name = alias != null ? alias.value() : field.getName();
fieldNameToValue.put(name, value);
}
}
return fieldNameToValue;
return Pair.pair(field, Optional.ofNullable(field.get(object)));
} catch (IllegalAccessException e) {
throw new EncodeException("Failure encoding object into query map", e);
}
}

private ObjectParamMetadata getMetadata(Class<?> objectType) {
ObjectParamMetadata metadata = classToMetadata.get(objectType);
if (metadata == null) {
metadata = ObjectParamMetadata.parseObjectType(objectType);
classToMetadata.put(objectType, metadata);
}
return metadata;
}

private static class ObjectParamMetadata {

private final List<Field> objectFields;
Expand All @@ -83,4 +86,20 @@ private static ObjectParamMetadata parseObjectType(Class<?> type) {
.collect(Collectors.toList()));
}
}

private static class Pair<T, U> {
private Pair(T left, U right) {
this.right = right;
this.left = left;
}

public final T left;
public final U right;

public static <T, U> Pair<T, U> pair(T left, U right) {
return new Pair<>(left, right);
}

}

}

0 comments on commit b53a535

Please sign in to comment.