You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2022/03/30 16:06:39 UTC

[nifi] branch main updated: NIFI-7254 Upgraded geoip2 from 2.12.0 to 2.16.1

This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 0f8183d  NIFI-7254 Upgraded geoip2 from 2.12.0 to 2.16.1
0f8183d is described below

commit 0f8183dd953c6f7bd173ba07618beaa0bf199c32
Author: Mike Thomsen <mt...@apache.org>
AuthorDate: Mon Mar 28 08:10:19 2022 -0400

    NIFI-7254 Upgraded geoip2 from 2.12.0 to 2.16.1
    
    - Replaced custom DatabaseReader with standard DatabaseReader implementation
    
    This closes #5909
    
    Signed-off-by: David Handermann <ex...@apache.org>
---
 .../nifi-enrich-processors/pom.xml                 |   7 +-
 .../apache/nifi/processors/AbstractEnrichIP.java   |   2 +-
 .../org/apache/nifi/processors/GeoEnrichIP.java    |   5 +-
 .../apache/nifi/processors/GeoEnrichIPRecord.java  |  17 +-
 .../org/apache/nifi/processors/ISPEnrichIP.java    |   5 +-
 .../nifi/processors/maxmind/DatabaseReader.java    | 272 ---------------------
 .../apache/nifi/processors/GeoEnrichTestUtils.java | 224 +++++++++++++----
 .../apache/nifi/processors/TestGeoEnrichIP.java    |   2 +-
 .../nifi/processors/TestGeoEnrichIPRecord.java     |  33 ++-
 .../apache/nifi/processors/TestISPEnrichIP.java    |  33 ++-
 .../nifi-lookup-services/pom.xml                   |   1 -
 .../apache/nifi/lookup/maxmind/DatabaseReader.java | 262 --------------------
 .../nifi/lookup/maxmind/IPLookupService.java       |   1 +
 nifi-nar-bundles/pom.xml                           |   6 +
 14 files changed, 239 insertions(+), 631 deletions(-)

diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/pom.xml b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/pom.xml
index b1e51e8..59b4781 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/pom.xml
@@ -41,7 +41,6 @@
         <dependency>
             <groupId>com.maxmind.geoip2</groupId>
             <artifactId>geoip2</artifactId>
-            <version>2.12.0</version>
             <exclusions>
                 <exclusion>
                     <groupId>com.google.code.findbugs</groupId>
@@ -91,6 +90,12 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.jr</groupId>
+            <artifactId>jackson-jr-objects</artifactId>
+            <version>${jackson.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-record-serialization-services</artifactId>
             <version>1.16.0-SNAPSHOT</version>
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/AbstractEnrichIP.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/AbstractEnrichIP.java
index 9711534..ab0fb9f 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/AbstractEnrichIP.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/AbstractEnrichIP.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
 import org.apache.nifi.components.PropertyDescriptor;
@@ -28,7 +29,6 @@ import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessorInitializationContext;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.util.StopWatch;
 
 import java.io.File;
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
index 8e657ec..763bd5b 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
@@ -16,6 +16,8 @@
  */
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
 import com.maxmind.geoip2.model.CityResponse;
 import com.maxmind.geoip2.record.Subdivision;
 import org.apache.commons.lang3.StringUtils;
@@ -32,7 +34,6 @@ import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.util.StopWatch;
 
 import java.io.IOException;
@@ -100,7 +101,7 @@ public class GeoEnrichIP extends AbstractEnrichIP {
         try {
             response = dbReader.city(inetAddress);
             stopWatch.stop();
-        } catch (final IOException ex) {
+        } catch (GeoIp2Exception | IOException ex) {
             // Note IOException is captured again as dbReader also makes InetAddress.getByName() calls.
             // Most name or IP resolutions failure should have been triggered in the try loop above but
             // environmental conditions may trigger errors during the second resolution as well.
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIPRecord.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIPRecord.java
index 4c83d50..5fff3a6 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIPRecord.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIPRecord.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.model.CityResponse;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
@@ -30,7 +31,6 @@ import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.record.path.FieldValue;
 import org.apache.nifi.record.path.RecordPath;
 import org.apache.nifi.record.path.RecordPathResult;
@@ -102,14 +102,6 @@ public class GeoEnrichIPRecord extends AbstractEnrichIP {
             .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR)
             .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
             .build();
-    public static final PropertyDescriptor GEO_ACCURACY = new PropertyDescriptor.Builder()
-            .name("geo-enrich-ip-accuracy-record-path")
-            .displayName("Accuracy Radius Record Path")
-            .description("Record path for putting the accuracy radius if provided by the database (in Kilometers)")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR)
-            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
-            .build();
     public static final PropertyDescriptor GEO_LATITUDE = new PropertyDescriptor.Builder()
             .name("geo-enrich-ip-latitude-record-path")
             .displayName("Latitude Record Path")
@@ -161,11 +153,11 @@ public class GeoEnrichIPRecord extends AbstractEnrichIP {
     )));
 
     public static final List<PropertyDescriptor> GEO_PROPERTIES = Collections.unmodifiableList(Arrays.asList(
-            GEO_CITY, GEO_ACCURACY, GEO_LATITUDE, GEO_LONGITUDE, GEO_COUNTRY, GEO_COUNTRY_ISO, GEO_POSTAL_CODE
+            GEO_CITY, GEO_LATITUDE, GEO_LONGITUDE, GEO_COUNTRY, GEO_COUNTRY_ISO, GEO_POSTAL_CODE
     ));
 
     private static final List<PropertyDescriptor> DESCRIPTORS = Collections.unmodifiableList(Arrays.asList(
-            GEO_DATABASE_FILE, READER, WRITER, SPLIT_FOUND_NOT_FOUND, IP_RECORD_PATH, GEO_CITY, GEO_ACCURACY, GEO_LATITUDE,
+            GEO_DATABASE_FILE, READER, WRITER, SPLIT_FOUND_NOT_FOUND, IP_RECORD_PATH, GEO_CITY, GEO_LATITUDE,
             GEO_LONGITUDE, GEO_COUNTRY, GEO_COUNTRY_ISO, GEO_POSTAL_CODE
     ));
 
@@ -321,14 +313,13 @@ public class GeoEnrichIPRecord extends AbstractEnrichIP {
         }
 
         boolean city = update(GEO_CITY, cached, record, response.getCity().getName());
-        boolean accuracy = update(GEO_ACCURACY, cached, record, response.getCity().getConfidence());
         boolean country = update(GEO_COUNTRY, cached, record, response.getCountry().getName());
         boolean iso = update(GEO_COUNTRY_ISO, cached, record, response.getCountry().getIsoCode());
         boolean lat = update(GEO_LATITUDE, cached, record, response.getLocation().getLatitude());
         boolean lon = update(GEO_LONGITUDE, cached, record, response.getLocation().getLongitude());
         boolean postal = update(GEO_POSTAL_CODE, cached, record, response.getPostal().getCode());
 
-        retVal = (city || accuracy || country || iso || lat || lon || postal);
+        retVal = (city || country || iso || lat || lon || postal);
 
         return retVal;
     }
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/ISPEnrichIP.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/ISPEnrichIP.java
index 781fb71..7365c2f 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/ISPEnrichIP.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/ISPEnrichIP.java
@@ -16,6 +16,8 @@
  */
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
 import com.maxmind.geoip2.model.IspResponse;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.annotation.behavior.EventDriven;
@@ -31,7 +33,6 @@ import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.util.StopWatch;
 
 import java.io.IOException;
@@ -93,7 +94,7 @@ public class ISPEnrichIP extends AbstractEnrichIP {
         try {
             response = dbReader.isp(inetAddress);
             stopWatch.stop();
-        } catch (final IOException ex) {
+        } catch (GeoIp2Exception | IOException ex) {
             // Note IOException is captured again as dbReader also makes InetAddress.getByName() calls.
             // Most name or IP resolutions failure should have been triggered in the try loop above but
             // environmental conditions may trigger errors during the second resolution as well.
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
deleted file mode 100644
index 8cbc74a..0000000
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.processors.maxmind;
-
-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.model.AnonymousIpResponse;
-import com.maxmind.geoip2.model.CityResponse;
-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;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * <p>
- * This class was copied from https://raw.githubusercontent.com/maxmind/GeoIP2-java/master/src/main/java/com/maxmind/geoip2/DatabaseReader.java It is written by Maxmind and it is available under
- * Apache Software License V2
- *
- * The modification we're making to the code below is to stop using exceptions for mainline flow control. Specifically we don't want to throw an exception simply because an address was not found.
- * </p>
- *
- * Instances of this class provide a reader for the GeoIP2 database format. IP addresses can be looked up using the <code>get</code> method.
- */
-public class DatabaseReader implements GeoIp2Provider, Closeable {
-
-    private final Reader reader;
-    private final ObjectMapper om;
-    private List<String> locales;
-
-    private DatabaseReader(final Builder builder) throws IOException {
-        if (builder.stream != null) {
-            this.reader = new Reader(builder.stream);
-        } else if (builder.database != null) {
-            this.reader = new Reader(builder.database, builder.mode);
-        } else {
-            // This should never happen. If it does, review the Builder class
-            // constructors for errors.
-            throw new IllegalArgumentException("Unsupported Builder configuration: expected either File or URL");
-        }
-
-        this.om = new ObjectMapper();
-        this.om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
-        this.om.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
-        InjectableValues inject = new InjectableValues.Std().addValue("locales", builder.locales);
-        this.om.setInjectableValues(inject);
-
-        this.locales = builder.locales;
-    }
-
-    /**
-     * <p>
-     * Constructs a Builder for the DatabaseReader. The file passed to it must be a valid GeoIP2 database file.
-     * </p>
-     *
-     * <p>
-     * <code>Builder</code> creates instances of <code>DatabaseReader</code> from values set by the methods.
-     * </p>
-     *
-     * <p>
-     * Only the values set in the <code>Builder</code> constructor are required.
-     * </p>
-     */
-    public final static class Builder {
-
-        final File database;
-        final InputStream stream;
-
-        List<String> locales = Arrays.asList("en");
-        FileMode mode = FileMode.MEMORY_MAPPED;
-
-        /**
-         * @param stream the stream containing the GeoIP2 database to use.
-         */
-        public Builder(InputStream stream) {
-            this.stream = stream;
-            this.database = null;
-        }
-
-        /**
-         * @param database the GeoIP2 database file to use.
-         */
-        public Builder(File database) {
-            this.database = database;
-            this.stream = null;
-        }
-
-        /**
-         * @param val List of locale codes to use in name property from most preferred to least preferred.
-         * @return Builder object
-         */
-        public Builder locales(List<String> val) {
-            this.locales = val;
-            return this;
-        }
-
-        /**
-         * @param val The file mode used to open the GeoIP2 database
-         * @return Builder object
-         * @throws java.lang.IllegalArgumentException if you initialized the Builder with a URL, which uses {@link FileMode#MEMORY}, but you provided a different FileMode to this method.
-         */
-        public Builder fileMode(FileMode val) {
-            if (this.stream != null && !FileMode.MEMORY.equals(val)) {
-                throw new IllegalArgumentException(
-                        "Only FileMode.MEMORY is supported when using an InputStream.");
-            }
-            this.mode = val;
-            return this;
-        }
-
-        /**
-         * @return an instance of <code>DatabaseReader</code> created from the fields set on this builder.
-         * @throws IOException if there is an error reading the database
-         */
-        public DatabaseReader build() throws IOException {
-            return new DatabaseReader(this);
-        }
-    }
-
-    /**
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @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 {
-        String databaseType = this.getMetadata().getDatabaseType();
-        if (!databaseType.contains(type)) {
-            String caller = Thread.currentThread().getStackTrace()[2].getMethodName();
-            throw new UnsupportedOperationException("Invalid attempt to open a " + databaseType + " database using the " + caller + " method");
-        }
-
-        ObjectNode node = (ObjectNode) this.reader.get(ipAddress);
-
-        if (node == null) {
-            return null;
-        }
-
-        InjectableValues inject = new JsonInjector(ipAddress.getHostAddress());
-        return this.om.reader(inject).treeToValue(node, cls);
-    }
-
-
-    /**
-     * <p>
-     * Closes the database.
-     * </p>
-     * <p>
-     * If you are using <code>FileMode.MEMORY_MAPPED</code>, this will
-     * <em>not</em> unmap the underlying file due to a limitation in Java's <code>MappedByteBuffer</code>. It will however set the reference to the buffer to <code>null</code>, allowing the garbage
-     * collector to collect it.
-     * </p>
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        this.reader.close();
-    }
-
-    @Override
-    public CountryResponse country(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, CountryResponse.class, true, "Country");
-    }
-
-    @Override
-    public CityResponse city(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, CityResponse.class, true, "City");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Anonymous IP.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a AnonymousIpResponse for the requested IP address.
-     * @throws IOException if there is an IO error
-     */
-    public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, AnonymousIpResponse.class, false, "GeoIP2-Anonymous-IP");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Connection Type database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a ConnectTypeResponse for the requested IP address.
-     * @throws IOException if there is an IO error
-     */
-    public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, ConnectionTypeResponse.class, false,"GeoIP2-Connection-Type");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Domain database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a DomainResponse for the requested IP address.
-     * @throws IOException if there is an IO error
-     */
-    public DomainResponse domain(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, DomainResponse.class, false, "GeoIP2-Domain");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 ISP database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return an IspResponse for the requested IP address.
-     * @throws IOException if there is an IO error
-     */
-    public IspResponse isp(InetAddress ipAddress) throws IOException {
-        return get(ipAddress, IspResponse.class, false, "GeoIP2-ISP");
-    }
-
-    /**
-     * @return the metadata for the open MaxMind DB file.
-     */
-    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);
-            } else if ("locales".equals(valueId)) {
-                return locales;
-            }
-
-            return null;
-        }
-    }
-}
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/GeoEnrichTestUtils.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/GeoEnrichTestUtils.java
index 834e99c..6aee2df 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/GeoEnrichTestUtils.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/GeoEnrichTestUtils.java
@@ -19,6 +19,7 @@ package org.apache.nifi.processors;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.InjectableValues;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jr.ob.JSON;
 import com.maxmind.geoip2.model.CityResponse;
 
 import java.util.Collections;
@@ -26,32 +27,93 @@ import java.util.Collections;
 public class GeoEnrichTestUtils {
     public static CityResponse getFullCityResponse() throws Exception {
         // Taken from MaxMind unit tests.
-        final String maxMindCityResponse = "{\"city\":{\"confidence\":76,"
-                + "\"geoname_id\":9876,\"names\":{\"en\":\"Minneapolis\""
-                + "}},\"continent\":{\"code\":\"NA\","
-                + "\"geoname_id\":42,\"names\":{" + "\"en\":\"North America\""
-                + "}},\"country\":{\"confidence\":99,"
-                + "\"iso_code\":\"US\",\"geoname_id\":1,\"names\":{"
-                + "\"en\":\"United States of America\"" + "}" + "},"
-                + "\"location\":{" + "\"accuracy_radius\":1500,"
-                + "\"latitude\":44.98," + "\"longitude\":93.2636,"
-                + "\"metro_code\":765," + "\"time_zone\":\"America/Chicago\""
-                + "}," + "\"postal\":{\"confidence\": 33, \"code\":\"55401\"},"
-                + "\"registered_country\":{" + "\"geoname_id\":2,"
-                + "\"iso_code\":\"CA\"," + "\"names\":{" + "\"en\":\"Canada\""
-                + "}" + "}," + "\"represented_country\":{" + "\"geoname_id\":3,"
-                + "\"iso_code\":\"GB\"," + "\"names\":{"
-                + "\"en\":\"United Kingdom\"" + "}," + "\"type\":\"C<military>\""
-                + "}," + "\"subdivisions\":[{" + "\"confidence\":88,"
-                + "\"geoname_id\":574635," + "\"iso_code\":\"MN\"," + "\"names\":{"
-                + "\"en\":\"Minnesota\"" + "}" + "}," + "{\"iso_code\":\"TT\"}],"
-                + "\"traits\":{" + "\"autonomous_system_number\":1234,"
-                + "\"autonomous_system_organization\":\"AS Organization\","
-                + "\"domain\":\"example.com\"," + "\"ip_address\":\"1.2.3.4\","
-                + "\"is_anonymous_proxy\":true,"
-                + "\"is_satellite_provider\":true," + "\"isp\":\"Comcast\","
-                + "\"organization\":\"Blorg\"," + "\"user_type\":\"college\""
-                + "}," + "\"maxmind\":{\"queries_remaining\":11}" + "}";
+        final String maxMindCityResponse = JSON.std
+                .composeString()
+                .startObject()
+                .startObjectField("maxmind")
+                .put("queries_remaining", 11)
+                .end()
+                .startObjectField("registered_country")
+                .put("geoname_id", 2)
+                .startObjectField("names")
+                .put("en", "Canada")
+                .end()
+                .put("is_in_european_union", false)
+                .put("iso_code", "CA")
+                .end()
+                .startObjectField("traits")
+                .put("is_anonymous_proxy", true)
+                .put("autonomous_system_number", 1234)
+                .put("isp", "Comcast")
+                .put("ip_address", "1.2.3.4")
+                .put("is_satellite_provider", true)
+                .put("autonomous_system_organization", "AS Organization")
+                .put("organization", "Blorg")
+                .put("domain", "example.com")
+                // These are here just to simplify the testing. We expect the
+                // difference
+                .put("is_anonymous", false)
+                .put("is_anonymous_vpn", false)
+                .put("is_hosting_provider", false)
+                .put("is_legitimate_proxy", false)
+                .put("is_public_proxy", false)
+                .put("is_residential_proxy", false)
+                .put("is_tor_exit_node", false)
+                .put("network", "1.2.3.0/24")
+                .end()
+                .startObjectField("country")
+                .startObjectField("names")
+                .put("en", "United States of America")
+                .end()
+                .put("geoname_id", 1)
+                .put("is_in_european_union", false)
+                .put("iso_code", "US")
+                .end()
+                .startObjectField("continent")
+                .startObjectField("names")
+                .put("en", "North America")
+                .end()
+                .put("code", "NA")
+                .put("geoname_id", 42)
+                .end()
+                .startObjectField("location")
+                .put("time_zone", "America/Chicago")
+                .put("metro_code", 765)
+                .put("latitude", 44.98)
+                .put("longitude", 93.2636)
+                .end()
+                .startArrayField("subdivisions")
+                .startObject()
+                .put("iso_code", "MN")
+                .put("geoname_id", 574635)
+                .startObjectField("names")
+                .put("en", "Minnesota")
+                .end()
+                .end()
+                .startObject()
+                .put("iso_code", "TT")
+                .end()
+                .end()
+                .startObjectField("represented_country")
+                .put("geoname_id", 3)
+                .startObjectField("names")
+                .put("en", "United Kingdom")
+                .end()
+                .put("type", "C<military>")
+                .put("is_in_european_union", true)
+                .put("iso_code", "GB")
+                .end()
+                .startObjectField("postal")
+                .put("code", "55401")
+                .end()
+                .startObjectField("city")
+                .put("geoname_id", 9876)
+                .startObjectField("names")
+                .put("en", "Minneapolis")
+                .end()
+                .end()
+                .end()
+                .finish();
 
         InjectableValues inject = new InjectableValues.Std().addValue("locales", Collections.singletonList("en"));
         ObjectMapper mapper = new ObjectMapper();
@@ -62,31 +124,91 @@ public class GeoEnrichTestUtils {
 
     public static CityResponse getNullLatAndLongCityResponse() throws Exception {
         // Taken from MaxMind unit tests and modified.
-        final String maxMindCityResponse = "{" + "\"city\":{" + "\"confidence\":76,"
-                + "\"geoname_id\":9876," + "\"names\":{" + "\"en\":\"Minneapolis\""
-                + "}" + "}," + "\"continent\":{" + "\"code\":\"NA\","
-                + "\"geoname_id\":42," + "\"names\":{" + "\"en\":\"North America\""
-                + "}" + "}," + "\"country\":{" + "\"confidence\":99,"
-                + "\"iso_code\":\"US\"," + "\"geoname_id\":1," + "\"names\":{"
-                + "\"en\":\"United States of America\"" + "}" + "},"
-                + "\"location\":{" + "\"accuracy_radius\":1500,"
-                + "\"metro_code\":765," + "\"time_zone\":\"America/Chicago\""
-                + "}," + "\"postal\":{\"confidence\": 33, \"code\":\"55401\"},"
-                + "\"registered_country\":{" + "\"geoname_id\":2,"
-                + "\"iso_code\":\"CA\"," + "\"names\":{" + "\"en\":\"Canada\""
-                + "}" + "}," + "\"represented_country\":{" + "\"geoname_id\":3,"
-                + "\"iso_code\":\"GB\"," + "\"names\":{"
-                + "\"en\":\"United Kingdom\"" + "}," + "\"type\":\"C<military>\""
-                + "}," + "\"subdivisions\":[{" + "\"confidence\":88,"
-                + "\"geoname_id\":574635," + "\"iso_code\":\"MN\"," + "\"names\":{"
-                + "\"en\":\"Minnesota\"" + "}" + "}," + "{\"iso_code\":\"TT\"}],"
-                + "\"traits\":{" + "\"autonomous_system_number\":1234,"
-                + "\"autonomous_system_organization\":\"AS Organization\","
-                + "\"domain\":\"example.com\"," + "\"ip_address\":\"1.2.3.4\","
-                + "\"is_anonymous_proxy\":true,"
-                + "\"is_satellite_provider\":true," + "\"isp\":\"Comcast\","
-                + "\"organization\":\"Blorg\"," + "\"user_type\":\"college\""
-                + "}," + "\"maxmind\":{\"queries_remaining\":11}" + "}";
+        final String maxMindCityResponse = JSON.std
+                .composeString()
+                .startObject()
+                .startObjectField("maxmind")
+                .put("queries_remaining", 11)
+                .end()
+                .startObjectField("registered_country")
+                .put("geoname_id", 2)
+                .startObjectField("names")
+                .put("en", "Canada")
+                .end()
+                .put("is_in_european_union", false)
+                .put("iso_code", "CA")
+                .end()
+                .startObjectField("traits")
+                .put("is_anonymous_proxy", true)
+                .put("autonomous_system_number", 1234)
+                .put("isp", "Comcast")
+                .put("ip_address", "1.2.3.4")
+                .put("is_satellite_provider", true)
+                .put("autonomous_system_organization", "AS Organization")
+                .put("organization", "Blorg")
+                .put("domain", "example.com")
+                // These are here just to simplify the testing. We expect the
+                // difference
+                .put("is_anonymous", false)
+                .put("is_anonymous_vpn", false)
+                .put("is_hosting_provider", false)
+                .put("is_legitimate_proxy", false)
+                .put("is_public_proxy", false)
+                .put("is_residential_proxy", false)
+                .put("is_tor_exit_node", false)
+                .put("network", "1.2.3.0/24")
+                .end()
+                .startObjectField("country")
+                .startObjectField("names")
+                .put("en", "United States of America")
+                .end()
+                .put("geoname_id", 1)
+                .put("is_in_european_union", false)
+                .put("iso_code", "US")
+                .end()
+                .startObjectField("continent")
+                .startObjectField("names")
+                .put("en", "North America")
+                .end()
+                .put("code", "NA")
+                .put("geoname_id", 42)
+                .end()
+                .startObjectField("location")
+                .put("time_zone", "America/Chicago")
+                .put("metro_code", 765)
+                .end()
+                .startArrayField("subdivisions")
+                .startObject()
+                .put("iso_code", "MN")
+                .put("geoname_id", 574635)
+                .startObjectField("names")
+                .put("en", "Minnesota")
+                .end()
+                .end()
+                .startObject()
+                .put("iso_code", "TT")
+                .end()
+                .end()
+                .startObjectField("represented_country")
+                .put("geoname_id", 3)
+                .startObjectField("names")
+                .put("en", "United Kingdom")
+                .end()
+                .put("type", "C<military>")
+                .put("is_in_european_union", true)
+                .put("iso_code", "GB")
+                .end()
+                .startObjectField("postal")
+                .put("code", "55401")
+                .end()
+                .startObjectField("city")
+                .put("geoname_id", 9876)
+                .startObjectField("names")
+                .put("en", "Minneapolis")
+                .end()
+                .end()
+                .end()
+                .finish();
 
         InjectableValues inject = new InjectableValues.Std().addValue("locales", Collections.singletonList("en"));
         ObjectMapper mapper = new ObjectMapper();
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIP.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIP.java
index f7a3255..ede5a39 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIP.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIP.java
@@ -16,11 +16,11 @@
  */
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.model.CityResponse;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.ProcessContext;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.util.MockFlowFile;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIPRecord.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIPRecord.java
index d80105a..301fc9a 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIPRecord.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestGeoEnrichIPRecord.java
@@ -17,6 +17,7 @@
 
 package org.apache.nifi.processors;
 
+import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.model.CityResponse;
 import org.apache.avro.Schema;
 import org.apache.commons.io.IOUtils;
@@ -27,7 +28,6 @@ import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.json.JsonRecordSetWriter;
 import org.apache.nifi.json.JsonTreeReader;
 import org.apache.nifi.processor.ProcessContext;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.schema.access.SchemaAccessUtils;
 import org.apache.nifi.serialization.RecordReaderFactory;
 import org.apache.nifi.serialization.RecordSetWriterFactory;
@@ -37,9 +37,8 @@ import org.apache.nifi.util.MockFlowFile;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.codehaus.jackson.map.ObjectMapper;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.io.InputStream;
 import java.net.InetAddress;
@@ -50,13 +49,15 @@ import java.util.List;
 import java.util.Map;
 
 import static org.apache.nifi.processors.GeoEnrichTestUtils.getFullCityResponse;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 public class TestGeoEnrichIPRecord {
     private TestRunner runner;
     private DatabaseReader reader;
-    @Before
+    @BeforeEach
     public void setup() throws Exception {
         reader = mock(DatabaseReader.class);
         final CityResponse cityResponse = getFullCityResponse();
@@ -90,7 +91,6 @@ public class TestGeoEnrichIPRecord {
         runner.enableControllerService(writer);
 
         runner.setProperty(GeoEnrichIPRecord.GEO_CITY, "/geo/city");
-        runner.setProperty(GeoEnrichIPRecord.GEO_ACCURACY, "/geo/accuracy");
         runner.setProperty(GeoEnrichIPRecord.GEO_COUNTRY, "/geo/country");
         runner.setProperty(GeoEnrichIPRecord.GEO_COUNTRY_ISO, "/geo/country_iso");
         runner.setProperty(GeoEnrichIPRecord.GEO_POSTAL_CODE, "/geo/country_postal");
@@ -131,20 +131,19 @@ public class TestGeoEnrichIPRecord {
         ObjectMapper mapper = new ObjectMapper();
         List<Map<String, Object>> result = (List<Map<String, Object>>)mapper.readValue(content, List.class);
 
-        Assert.assertNotNull(result);
-        Assert.assertEquals(1, result.size());
+        assertNotNull(result);
+        assertEquals(1, result.size());
 
         Map<String, Object> element = result.get(0);
         Map<String, Object> geo = (Map<String, Object>) element.get("geo");
 
-        Assert.assertNotNull(geo);
-        Assert.assertNotNull(geo.get("accuracy"));
-        Assert.assertNotNull(geo.get("city"));
-        Assert.assertNotNull(geo.get("country"));
-        Assert.assertNotNull(geo.get("country_iso"));
-        Assert.assertNotNull(geo.get("country_postal"));
-        Assert.assertNotNull(geo.get("lat"));
-        Assert.assertNotNull(geo.get("lon"));
+        assertNotNull(geo);
+        assertNotNull(geo.get("city"));
+        assertNotNull(geo.get("country"));
+        assertNotNull(geo.get("country_iso"));
+        assertNotNull(geo.get("country_postal"));
+        assertNotNull(geo.get("lat"));
+        assertNotNull(geo.get("lon"));
     }
 
     class TestableGeoEnrichIPRecord extends GeoEnrichIPRecord {
@@ -153,7 +152,7 @@ public class TestGeoEnrichIPRecord {
         @Override
         protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
             return Collections.unmodifiableList(Arrays.asList(
-                    READER, WRITER, IP_RECORD_PATH, SPLIT_FOUND_NOT_FOUND, GEO_CITY, GEO_ACCURACY, GEO_LATITUDE, GEO_LONGITUDE, GEO_COUNTRY, GEO_COUNTRY_ISO, GEO_POSTAL_CODE
+                    READER, WRITER, IP_RECORD_PATH, SPLIT_FOUND_NOT_FOUND, GEO_CITY, GEO_LATITUDE, GEO_LONGITUDE, GEO_COUNTRY, GEO_COUNTRY_ISO, GEO_POSTAL_CODE
             ));
         }
         @OnScheduled
diff --git a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestISPEnrichIP.java b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestISPEnrichIP.java
index cf2ed04..e3eab7e 100644
--- a/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestISPEnrichIP.java
+++ b/nifi-nar-bundles/nifi-enrich-bundle/nifi-enrich-processors/src/test/java/org/apache/nifi/processors/TestISPEnrichIP.java
@@ -19,11 +19,12 @@ package org.apache.nifi.processors;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.InjectableValues;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.jr.ob.JSON;
+import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.model.IspResponse;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.ProcessContext;
-import org.apache.nifi.processors.maxmind.DatabaseReader;
 import org.apache.nifi.util.MockFlowFile;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
@@ -255,7 +256,7 @@ public class TestISPEnrichIP {
     }
 
     private IspResponse getIspResponse(final String ipAddress) throws Exception {
-        final String maxMindIspResponse = "{\n" +
+        String maxMindIspResponse = "{\n" +
             "         \"isp\" : \"Apache NiFi - Test ISP\",\n" +
             "         \"organization\" : \"Apache NiFi - Test Organization\",\n" +
             "         \"autonomous_system_number\" : 1337,\n" +
@@ -263,6 +264,18 @@ public class TestISPEnrichIP {
             "         \"ip_address\" : \"" + ipAddress + "\"\n" +
             "      }\n";
 
+        maxMindIspResponse = JSON.std
+                .composeString()
+                .startObject()
+                .put("autonomous_system_number", 1337)
+                .put("autonomous_system_organization", "Apache NiFi - Test Chocolate")
+                .put("isp", "Apache NiFi - Test ISP")
+                .put("organization", "Apache NiFi - Test Organization")
+                .put("ip_address", "1.1.1.1")
+                .put("network", "1.1.1.0/24")
+                .end()
+                .finish();
+
         InjectableValues inject = new InjectableValues.Std().addValue("locales", Collections.singletonList("en"));
         ObjectMapper mapper = new ObjectMapper();
         mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -273,12 +286,16 @@ public class TestISPEnrichIP {
     }
 
     private IspResponse getIspResponseWithoutASNDetail(final String ipAddress) throws Exception {
-        final String maxMindIspResponse = "{\n" +
-            "         \"isp\" : \"Apache NiFi - Test ISP\",\n" +
-            "         \"organization\" : \"Apache NiFi - Test Organization\",\n" +
-            "         \"autonomous_system_number\" : null,\n" +
-            "         \"ip_address\" : \"" + ipAddress + "\"\n" +
-            "      }\n";
+        final String maxMindIspResponse = JSON.std
+                .composeString()
+                .startObject()
+                .put("autonomous_system_number", null)
+                .put("isp", "Apache NiFi - Test ISP")
+                .put("organization", "Apache NiFi - Test Organization")
+                .put("ip_address", ipAddress)
+                .put("network", "1.1.1.0/24")
+                .end()
+                .finish();
 
         InjectableValues inject = new InjectableValues.Std().addValue("locales", Collections.singletonList("en"));
         ObjectMapper mapper = new ObjectMapper();
diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/pom.xml
index a2ccf2c..b57e342 100644
--- a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/pom.xml
+++ b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/pom.xml
@@ -75,7 +75,6 @@
         <dependency>
             <groupId>com.maxmind.geoip2</groupId>
             <artifactId>geoip2</artifactId>
-            <version>2.12.0</version>
             <exclusions>
                 <exclusion>
                     <groupId>com.google.code.findbugs</groupId>
diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/DatabaseReader.java b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/DatabaseReader.java
deleted file mode 100644
index 93fe31e..0000000
--- a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/DatabaseReader.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.lookup.maxmind;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetAddress;
-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.GeoIp2Exception;
-import com.maxmind.geoip2.model.AnonymousIpResponse;
-import com.maxmind.geoip2.model.CityResponse;
-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>
- * This class was copied from https://raw.githubusercontent.com/maxmind/GeoIP2-java/master/src/main/java/com/maxmind/geoip2/DatabaseReader.java It is written by Maxmind and it is available under
- * Apache Software License V2
- *
- * The modification we're making to the code below is to stop using exceptions for mainline flow control. Specifically we don't want to throw an exception simply because an address was not found.
- * </p>
- *
- * Instances of this class provide a reader for the GeoIP2 database format. IP addresses can be looked up using the <code>get</code> method.
- */
-public class DatabaseReader implements GeoIp2Provider, Closeable {
-
-    private final Reader reader;
-    private final ObjectMapper om;
-
-    private DatabaseReader(final Builder builder) throws IOException {
-        if (builder.stream != null) {
-            this.reader = new Reader(builder.stream);
-        } else if (builder.database != null) {
-            this.reader = new Reader(builder.database, builder.mode);
-        } else {
-            // This should never happen. If it does, review the Builder class
-            // constructors for errors.
-            throw new IllegalArgumentException("Unsupported Builder configuration: expected either File or URL");
-        }
-
-        this.om = new ObjectMapper();
-        this.om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
-        this.om.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
-        InjectableValues inject = new InjectableValues.Std().addValue("locales", builder.locales);
-        this.om.setInjectableValues(inject);
-    }
-
-    /**
-     * <p>
-     * Constructs a Builder for the DatabaseReader. The file passed to it must be a valid GeoIP2 database file.
-     * </p>
-     * <p>
-     * <code>Builder</code> creates instances of <code>DatabaseReader</code> from values set by the methods.
-     * </p>
-     * <p>
-     * Only the values set in the <code>Builder</code> constructor are required.
-     * </p>
-     */
-    public final static class Builder {
-
-        final File database;
-        final InputStream stream;
-
-        List<String> locales = Collections.singletonList("en");
-        FileMode mode = FileMode.MEMORY_MAPPED;
-
-        /**
-         * @param stream the stream containing the GeoIP2 database to use.
-         */
-        public Builder(InputStream stream) {
-            this.stream = stream;
-            this.database = null;
-        }
-
-        /**
-         * @param database the GeoIP2 database file to use.
-         */
-        public Builder(File database) {
-            this.database = database;
-            this.stream = null;
-        }
-
-        /**
-         * @param val List of locale codes to use in name property from most preferred to least preferred.
-         * @return Builder object
-         */
-        public Builder locales(List<String> val) {
-            this.locales = val;
-            return this;
-        }
-
-        /**
-         * @param val The file mode used to open the GeoIP2 database
-         * @return Builder object
-         * @throws java.lang.IllegalArgumentException if you initialized the Builder with a URL, which uses {@link FileMode#MEMORY}, but you provided a different FileMode to this method.
-         */
-        public Builder fileMode(FileMode val) {
-            if (this.stream != null && !FileMode.MEMORY.equals(val)) {
-                throw new IllegalArgumentException("Only FileMode.MEMORY is supported when using an InputStream.");
-            }
-
-            this.mode = val;
-            return this;
-        }
-
-        /**
-         * @return an instance of <code>DatabaseReader</code> created from the fields set on this builder.
-         * @throws IOException if there is an error reading the database
-         */
-        public DatabaseReader build() throws IOException {
-            return new DatabaseReader(this);
-        }
-    }
-
-    /**
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @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 {
-        ObjectNode node = (ObjectNode) this.reader.get(ipAddress);
-        if (node == null) {
-            return null;
-        }
-
-        InjectableValues inject = new JsonInjector(ipAddress.getHostAddress());
-        return this.om.reader(inject).treeToValue(node, cls);
-    }
-
-    /**
-     * <p>
-     * Closes the database.
-     * </p>
-     * <p>
-     * If you are using <code>FileMode.MEMORY_MAPPED</code>, this will
-     * <em>not</em> unmap the underlying file due to a limitation in Java's <code>MappedByteBuffer</code>. It will however set the reference to the buffer to <code>null</code>, allowing the garbage
-     * collector to collect it.
-     * </p>
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        this.reader.close();
-    }
-
-    @Override
-    public CountryResponse country(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, CountryResponse.class, true, "Country");
-    }
-
-    @Override
-    public CityResponse city(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, CityResponse.class, true, "City");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Anonymous IP.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a AnonymousIpResponse for the requested IP address.
-     * @throws IOException if there is an IO error
-     */
-    public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, AnonymousIpResponse.class, false, "GeoIP2-Anonymous-IP");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Connection Type database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a ConnectTypeResponse 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 ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, ConnectionTypeResponse.class, false,
-            "GeoIP2-Connection-Type");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 Domain database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return a DomainResponse 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 DomainResponse domain(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, DomainResponse.class, false, "GeoIP2-Domain");
-    }
-
-    /**
-     * Look up an IP address in a GeoIP2 ISP database.
-     *
-     * @param ipAddress IPv4 or IPv6 address to lookup.
-     * @return an IspResponse 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 IspResponse isp(InetAddress ipAddress) throws IOException {
-        return this.get(ipAddress, IspResponse.class, false, "GeoIP2-ISP");
-    }
-
-    /**
-     * @return the metadata for the open MaxMind DB file.
-     */
-    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;
-        }
-    }
-}
diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/IPLookupService.java b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/IPLookupService.java
index 819b90f..bd93a1e 100644
--- a/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/IPLookupService.java
+++ b/nifi-nar-bundles/nifi-standard-services/nifi-lookup-services-bundle/nifi-lookup-services/src/main/java/org/apache/nifi/lookup/maxmind/IPLookupService.java
@@ -18,6 +18,7 @@
 package org.apache.nifi.lookup.maxmind;
 
 import com.maxmind.db.InvalidDatabaseException;
+import com.maxmind.geoip2.DatabaseReader;
 import com.maxmind.geoip2.model.AnonymousIpResponse;
 import com.maxmind.geoip2.model.CityResponse;
 import com.maxmind.geoip2.model.ConnectionTypeResponse;
diff --git a/nifi-nar-bundles/pom.xml b/nifi-nar-bundles/pom.xml
index 79acbfe..b5443ad 100755
--- a/nifi-nar-bundles/pom.xml
+++ b/nifi-nar-bundles/pom.xml
@@ -358,6 +358,12 @@
                     </exclusion>
                 </exclusions>
             </dependency>
+
+            <dependency>
+                <groupId>com.maxmind.geoip2</groupId>
+                <artifactId>geoip2</artifactId>
+                <version>2.16.1</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 </project>