NIFI-8492: Addressed issue in DatabaseReader class of IPLookupService that was attempting to set values on the JSON returned by MaxMind. Instead of modifying the object directly, we should use an Injectable in the Reader so that the value read will have the appropriate values but we don't need to modify those objects returned by MaxMind. Similar solution of NIFI-5814

Signed-off-by: Nathan Gough <thenatog@gmail.com>

This closes #5777.
This commit is contained in:
Matthew Burgess 2022-02-16 18:07:57 -05:00 committed by Nathan Gough
parent a837397d56
commit 2f3380390d
1 changed files with 35 additions and 25 deletions

View File

@ -21,18 +21,20 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.maxmind.db.Metadata;
import com.maxmind.db.Reader;
import com.maxmind.db.Reader.FileMode;
import com.maxmind.geoip2.GeoIp2Provider;
import com.maxmind.geoip2.exception.AddressNotFoundException;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.AnonymousIpResponse;
import com.maxmind.geoip2.model.CityResponse;
@ -40,6 +42,7 @@ import com.maxmind.geoip2.model.ConnectionTypeResponse;
import com.maxmind.geoip2.model.CountryResponse;
import com.maxmind.geoip2.model.DomainResponse;
import com.maxmind.geoip2.model.IspResponse;
import com.maxmind.geoip2.record.Traits;
/**
* <p>
@ -56,7 +59,7 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
private final Reader reader;
private final ObjectMapper om;
private DatabaseReader(Builder builder) throws IOException {
private DatabaseReader(final Builder builder) throws IOException {
if (builder.stream != null) {
this.reader = new Reader(builder.stream);
} else if (builder.database != null) {
@ -90,7 +93,7 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
final File database;
final InputStream stream;
List<String> locales = Arrays.asList("en");
List<String> locales = Collections.singletonList("en");
FileMode mode = FileMode.MEMORY_MAPPED;
/**
@ -146,25 +149,14 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
* @return An object of type T with the data for the IP address or null if no information could be found for the given IP address
* @throws IOException if there is an error opening or reading from the file.
*/
private <T> T get(InetAddress ipAddress, Class<T> cls, boolean hasTraits, String type) throws IOException, AddressNotFoundException {
private <T> T get(InetAddress ipAddress, Class<T> cls, boolean hasTraits, String type) throws IOException {
ObjectNode node = (ObjectNode) this.reader.get(ipAddress);
if (node == null) {
return null;
}
ObjectNode ipNode;
if (hasTraits) {
if (!node.has("traits")) {
node.set("traits", this.om.createObjectNode());
}
ipNode = (ObjectNode) node.get("traits");
} else {
ipNode = node;
}
ipNode.put("ip_address", ipAddress.getHostAddress());
return this.om.treeToValue(node, cls);
InjectableValues inject = new JsonInjector(ipAddress.getHostAddress());
return this.om.reader(inject).treeToValue(node, cls);
}
/**
@ -185,12 +177,12 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
}
@Override
public CountryResponse country(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public CountryResponse country(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, CountryResponse.class, true, "Country");
}
@Override
public CityResponse city(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public CityResponse city(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, CityResponse.class, true, "City");
}
@ -199,10 +191,9 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
*
* @param ipAddress IPv4 or IPv6 address to lookup.
* @return a AnonymousIpResponse for the requested IP address.
* @throws GeoIp2Exception if there is an error looking up the IP
* @throws IOException if there is an IO error
*/
public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, AnonymousIpResponse.class, false, "GeoIP2-Anonymous-IP");
}
@ -214,7 +205,7 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
* @throws GeoIp2Exception if there is an error looking up the IP
* @throws IOException if there is an IO error
*/
public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, ConnectionTypeResponse.class, false,
"GeoIP2-Connection-Type");
}
@ -227,7 +218,7 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
* @throws GeoIp2Exception if there is an error looking up the IP
* @throws IOException if there is an IO error
*/
public DomainResponse domain(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public DomainResponse domain(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, DomainResponse.class, false, "GeoIP2-Domain");
}
@ -239,7 +230,7 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
* @throws GeoIp2Exception if there is an error looking up the IP
* @throws IOException if there is an IO error
*/
public IspResponse isp(InetAddress ipAddress) throws IOException, GeoIp2Exception {
public IspResponse isp(InetAddress ipAddress) throws IOException {
return this.get(ipAddress, IspResponse.class, false, "GeoIP2-ISP");
}
@ -249,4 +240,23 @@ public class DatabaseReader implements GeoIp2Provider, Closeable {
public Metadata getMetadata() {
return this.reader.getMetadata();
}
private class JsonInjector extends InjectableValues {
private final String ip;
JsonInjector(final String ip) {
this.ip = ip;
}
@Override
public Object findInjectableValue(final Object valueId, final DeserializationContext ctxt, final BeanProperty forProperty, final Object beanInstance) throws JsonMappingException {
if ("ip_address".equals(valueId)) {
return ip;
} else if ("traits".equals(valueId)) {
return new Traits(ip);
}
return null;
}
}
}