From 491b003708128de2b3d2b77a0346e44d54bf01f1 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Mon, 13 Oct 2025 12:31:43 -0700 Subject: [PATCH 01/17] Move model and record classes to records This will eventually reduce boilerplate and provide a more consistent experience for users. I am leaving the old getter methods as deprecated for one major version to give users the ability to upgrade before having to migrate to the updated accessors. There are other breaking changes, but these will likely not affect most users. --- CHANGELOG.md | 12 + ...tractRecord.java => JsonSerializable.java} | 21 +- .../java/com/maxmind/geoip2/NamedRecord.java | 47 ++ .../geoip2/model/AbstractCityResponse.java | 131 ----- .../geoip2/model/AbstractCountryResponse.java | 108 ----- .../geoip2/model/AbstractResponse.java | 40 -- .../geoip2/model/AnonymousIpResponse.java | 102 ++-- .../geoip2/model/AnonymousPlusResponse.java | 166 +++++-- .../com/maxmind/geoip2/model/AsnResponse.java | 78 +-- .../maxmind/geoip2/model/CityResponse.java | 290 +++++++++-- .../geoip2/model/ConnectionTypeResponse.java | 64 +-- .../maxmind/geoip2/model/CountryResponse.java | 140 +++++- .../maxmind/geoip2/model/DomainResponse.java | 58 ++- .../geoip2/model/EnterpriseResponse.java | 290 +++++++++-- .../geoip2/model/InsightsResponse.java | 257 ++++++++-- .../maxmind/geoip2/model/IpBaseResponse.java | 125 ----- .../maxmind/geoip2/model/IpRiskResponse.java | 118 +++-- .../com/maxmind/geoip2/model/IspResponse.java | 173 +++++-- .../geoip2/record/AbstractNamedRecord.java | 58 --- .../java/com/maxmind/geoip2/record/City.java | 98 +++- .../com/maxmind/geoip2/record/Continent.java | 93 +++- .../com/maxmind/geoip2/record/Country.java | 129 +++-- .../com/maxmind/geoip2/record/Location.java | 127 ++--- .../com/maxmind/geoip2/record/MaxMind.java | 34 +- .../com/maxmind/geoip2/record/Postal.java | 51 +- .../geoip2/record/RepresentedCountry.java | 145 +++++- .../maxmind/geoip2/record/Subdivision.java | 114 +++-- .../com/maxmind/geoip2/record/Traits.java | 452 +++++++++--------- .../maxmind/geoip2/DatabaseReaderTest.java | 114 ++--- .../maxmind/geoip2/WebServiceClientTest.java | 117 ++--- .../maxmind/geoip2/matchers/CodeMatcher.java | 2 +- .../geoip2/matchers/HttpStatusMatcher.java | 2 +- .../geoip2/model/CityResponseTest.java | 26 +- .../geoip2/model/CountryResponseTest.java | 72 +-- .../geoip2/model/InsightsResponseTest.java | 122 +++-- .../com/maxmind/geoip2/model/JsonTest.java | 2 +- 36 files changed, 2448 insertions(+), 1530 deletions(-) rename src/main/java/com/maxmind/geoip2/{record/AbstractRecord.java => JsonSerializable.java} (61%) create mode 100644 src/main/java/com/maxmind/geoip2/NamedRecord.java delete mode 100644 src/main/java/com/maxmind/geoip2/model/AbstractCityResponse.java delete mode 100644 src/main/java/com/maxmind/geoip2/model/AbstractCountryResponse.java delete mode 100644 src/main/java/com/maxmind/geoip2/model/AbstractResponse.java delete mode 100644 src/main/java/com/maxmind/geoip2/model/IpBaseResponse.java delete mode 100644 src/main/java/com/maxmind/geoip2/record/AbstractNamedRecord.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ebb0390..258b83c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ CHANGELOG 5.0.0 ------------------ +* **BREAKING:** All model and record classes have been converted to Java records. + This provides a more modern, immutable data model with automatic implementations + of `equals()`, `hashCode()`, and `toString()`. The abstract classes + `AbstractRecord`, `AbstractNamedRecord`, `AbstractResponse`, + `AbstractCountryResponse`, `AbstractCityResponse`, and `IpBaseResponse` have + been removed. Record components can be accessed using the new accessor methods + (e.g., `city()`, `country()`, `location()`). The traditional getter methods + (e.g., `getCity()`, `getCountry()`, `getLocation()`) are still available but + have been deprecated and will be removed in version 6.0.0. +* **BREAKING:** `RepresentedCountry` is now a separate record type instead of + extending `Country`. It shares the same fields as `Country` but adds a `type` + field. * The deprecation notices for IP Risk database support have been removed. IP Risk database support will continue to be maintained. * **BREAKING:** The deprecated `WebServiceClient.Builder` methods diff --git a/src/main/java/com/maxmind/geoip2/record/AbstractRecord.java b/src/main/java/com/maxmind/geoip2/JsonSerializable.java similarity index 61% rename from src/main/java/com/maxmind/geoip2/record/AbstractRecord.java rename to src/main/java/com/maxmind/geoip2/JsonSerializable.java index 631d795dc..46dd37ac9 100644 --- a/src/main/java/com/maxmind/geoip2/record/AbstractRecord.java +++ b/src/main/java/com/maxmind/geoip2/JsonSerializable.java @@ -1,23 +1,26 @@ -package com.maxmind.geoip2.record; +package com.maxmind.geoip2; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; /** - * Abstract class for GeoIP2. + * Interface for classes that can be serialized to JSON. + * Provides default implementation for toJson() method. */ -public abstract class AbstractRecord { +public interface JsonSerializable { /** * @return JSON representation of this object. The structure is the same as * the JSON provided by the GeoIP2 web service. * @throws IOException if there is an error serializing the object to JSON. */ - public String toJson() throws IOException { + default String toJson() throws IOException { JsonMapper mapper = JsonMapper.builder() .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) + .addModule(new JavaTimeModule()) .serializationInclusion(JsonInclude.Include.NON_NULL) .serializationInclusion(JsonInclude.Include.NON_EMPTY) .build(); @@ -25,14 +28,4 @@ public String toJson() throws IOException { return mapper.writeValueAsString(this); } - @Override - public String toString() { - // This exception should never happen. If it does happen, we did - // something wrong. - try { - return getClass().getName() + " [ " + toJson() + " ]"; - } catch (IOException e) { - throw new RuntimeException(e); - } - } } diff --git a/src/main/java/com/maxmind/geoip2/NamedRecord.java b/src/main/java/com/maxmind/geoip2/NamedRecord.java new file mode 100644 index 000000000..dd21b1413 --- /dev/null +++ b/src/main/java/com/maxmind/geoip2/NamedRecord.java @@ -0,0 +1,47 @@ +package com.maxmind.geoip2; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; + +/** + * Interface for record classes that have localized names and GeoName IDs. + * Provides a default implementation for the name() method that returns the name + * in the first available locale. + */ +public interface NamedRecord extends JsonSerializable { + + /** + * @return The GeoName ID for this location. + */ + @JsonProperty("geoname_id") + Long geonameId(); + + /** + * @return A {@link Map} from locale codes to the name in that locale. + */ + @JsonProperty("names") + Map names(); + + /** + * @return The list of locales to use for name lookups. + */ + @JsonIgnore + List locales(); + + /** + * @return The name based on the locales list. Returns the name in the first + * locale for which a name is available. If no name is available in any of the + * specified locales, returns null. + */ + @JsonIgnore + default String name() { + for (String lang : locales()) { + if (names().containsKey(lang)) { + return names().get(lang); + } + } + return null; + } +} diff --git a/src/main/java/com/maxmind/geoip2/model/AbstractCityResponse.java b/src/main/java/com/maxmind/geoip2/model/AbstractCityResponse.java deleted file mode 100644 index 6cc4bec2f..000000000 --- a/src/main/java/com/maxmind/geoip2/model/AbstractCityResponse.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.maxmind.geoip2.model; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.maxmind.db.Network; -import com.maxmind.geoip2.record.City; -import com.maxmind.geoip2.record.Continent; -import com.maxmind.geoip2.record.Country; -import com.maxmind.geoip2.record.Location; -import com.maxmind.geoip2.record.MaxMind; -import com.maxmind.geoip2.record.Postal; -import com.maxmind.geoip2.record.RepresentedCountry; -import com.maxmind.geoip2.record.Subdivision; -import com.maxmind.geoip2.record.Traits; -import java.util.ArrayList; -import java.util.List; - -/** - * Abstract class for models that contain City data. - */ -public abstract class AbstractCityResponse extends AbstractCountryResponse { - - private final City city; - private final Location location; - private final Postal postal; - private final List subdivisions; - - AbstractCityResponse( - City city, - Continent continent, - Country country, - Location location, - MaxMind maxmind, - Postal postal, - Country registeredCountry, - RepresentedCountry representedCountry, - List subdivisions, - Traits traits - ) { - super(continent, country, maxmind, registeredCountry, representedCountry, traits); - this.city = city != null ? city : new City(); - this.location = location != null ? location : new Location(); - this.postal = postal != null ? postal : new Postal(); - this.subdivisions = subdivisions != null ? subdivisions : new ArrayList<>(); - } - - AbstractCityResponse( - AbstractCityResponse response, - String ipAddress, - Network network, - List locales - ) { - super(response, ipAddress, network, locales); - // The response fields will be non-null because of the above - // constructor used during deserializing. - this.city = new City(response.getCity(), locales); - this.location = response.getLocation(); - this.postal = response.getPostal(); - this.subdivisions = mapSubdivisions(response.getSubdivisions(), locales); - } - - private static ArrayList mapSubdivisions( - List subdivisions, - List locales - ) { - ArrayList subdivisions2 = new ArrayList<>(subdivisions.size()); - for (Subdivision subdivision : subdivisions) { - subdivisions2.add(new Subdivision(subdivision, locales)); - } - return subdivisions2; - } - - /** - * @return City record for the requested IP address. - */ - public City getCity() { - return this.city; - } - - /** - * @return Location record for the requested IP address. - */ - public Location getLocation() { - return this.location; - } - - /** - * @return the postal - */ - public Postal getPostal() { - return this.postal; - } - - /** - * @return An {@link List} of {@link Subdivision} objects representing the - * country subdivisions for the requested IP address. The number and - * type of subdivisions varies by country, but a subdivision is - * typically a state, province, county, etc. Subdivisions are - * ordered from most general (largest) to most specific (smallest). - * If the response did not contain any subdivisions, this method - * returns an empty array. - */ - public List getSubdivisions() { - return new ArrayList<>(this.subdivisions); - } - - /** - * @return An object representing the most specific subdivision returned. If - * the response did not contain any subdivisions, this method - * returns an empty {@link Subdivision} object. - */ - @JsonIgnore - public Subdivision getMostSpecificSubdivision() { - if (this.subdivisions.isEmpty()) { - return new Subdivision(); - } - return this.subdivisions.get(this.subdivisions.size() - 1); - } - - /** - * @return An object representing the least specific subdivision returned. If - * the response did not contain any subdivisions, this method - * returns an empty {@link Subdivision} object. - */ - @JsonIgnore - public Subdivision getLeastSpecificSubdivision() { - if (this.subdivisions.isEmpty()) { - return new Subdivision(); - } - return this.subdivisions.get(0); - } -} diff --git a/src/main/java/com/maxmind/geoip2/model/AbstractCountryResponse.java b/src/main/java/com/maxmind/geoip2/model/AbstractCountryResponse.java deleted file mode 100644 index 615e17653..000000000 --- a/src/main/java/com/maxmind/geoip2/model/AbstractCountryResponse.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.maxmind.geoip2.model; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.Network; -import com.maxmind.geoip2.record.Continent; -import com.maxmind.geoip2.record.Country; -import com.maxmind.geoip2.record.MaxMind; -import com.maxmind.geoip2.record.RepresentedCountry; -import com.maxmind.geoip2.record.Traits; -import java.util.List; - -/** -* Abstract class for models that contain Country data. - */ -public abstract class AbstractCountryResponse extends AbstractResponse { - - private final Continent continent; - private final Country country; - private final Country registeredCountry; - private final MaxMind maxmind; - private final RepresentedCountry representedCountry; - private final Traits traits; - - AbstractCountryResponse( - Continent continent, - Country country, - MaxMind maxmind, - Country registeredCountry, - RepresentedCountry representedCountry, - Traits traits - ) { - this.continent = continent != null ? continent : new Continent(); - this.country = country != null ? country : new Country(); - this.registeredCountry = registeredCountry != null ? registeredCountry : new Country(); - this.maxmind = maxmind != null ? maxmind : new MaxMind(); - this.representedCountry = - representedCountry != null ? representedCountry : new RepresentedCountry(); - this.traits = traits != null ? traits : new Traits(); - } - - AbstractCountryResponse( - AbstractCountryResponse response, - String ipAddress, - Network network, - List locales - ) { - // The response fields will be non-null because of the above - // constructor used during deserializing. - this.continent = new Continent(response.getContinent(), locales); - this.country = new Country(response.getCountry(), locales); - this.maxmind = response.getMaxMind(); - this.registeredCountry = new Country(response.getRegisteredCountry(), locales); - this.representedCountry = new RepresentedCountry(response.getRepresentedCountry(), locales); - this.traits = new Traits(response.getTraits(), ipAddress, network); - } - - /** - * @return MaxMind record containing data related to your account. - */ - @JsonProperty("maxmind") - public MaxMind getMaxMind() { - return this.maxmind; - } - - /** - * @return Registered country record for the requested IP address. This - * record represents the country where the ISP has registered a - * given IP block and may differ from the user's country. - */ - @JsonProperty("registered_country") - public Country getRegisteredCountry() { - return this.registeredCountry; - } - - /** - * @return Continent record for the requested IP address. - */ - public Continent getContinent() { - return this.continent; - } - - /** - * @return Country record for the requested IP address. This object - * represents the country where MaxMind believes the end user is - * located. - */ - public Country getCountry() { - return this.country; - } - - /** - * @return Represented country record for the requested IP address. The - * represented country is used for things like military bases. It is - * only present when the represented country differs from the - * country. - */ - @JsonProperty("represented_country") - public RepresentedCountry getRepresentedCountry() { - return this.representedCountry; - } - - /** - * @return Record for the traits of the requested IP address. - */ - public Traits getTraits() { - return this.traits; - } -} diff --git a/src/main/java/com/maxmind/geoip2/model/AbstractResponse.java b/src/main/java/com/maxmind/geoip2/model/AbstractResponse.java deleted file mode 100644 index 6c742f1bb..000000000 --- a/src/main/java/com/maxmind/geoip2/model/AbstractResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.maxmind.geoip2.model; - -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.io.IOException; - -/** - * Abstract class for GeoIP2 models. - */ -public abstract class AbstractResponse { - - /** - * @return JSON representation of this object. The structure is the same as - * the JSON provided by the GeoIP2 web service. - * @throws IOException if there is an error serializing the object to JSON. - */ - public String toJson() throws IOException { - JsonMapper mapper = JsonMapper.builder() - .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) - .addModule(new JavaTimeModule()) - .serializationInclusion(Include.NON_NULL) - .serializationInclusion(Include.NON_EMPTY) - .build(); - - return mapper.writeValueAsString(this); - } - - @Override - public String toString() { - // This exception should never happen. If it does happen, we did - // something wrong. - try { - return getClass().getName() + " [ " + toJson() + " ]"; - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java index 923023b98..573e04765 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java @@ -3,45 +3,68 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoIP2 Anonymous IP model. + * + * @param ipAddress The IP address that the data in the model is for. + * @param isAnonymous Whether the IP address belongs to any sort of anonymous network. + * @param isAnonymousVpn Whether the IP address is registered to an anonymous VPN provider. If a + * VPN provider does not register subnets under names associated with them, + * we will likely only flag their IP ranges using isHostingProvider. + * @param isHostingProvider Whether the IP address belongs to a hosting or VPN provider (see + * description of isAnonymousVpn). + * @param isPublicProxy Whether the IP address belongs to a public proxy. + * @param isResidentialProxy Whether the IP address is on a suspected anonymizing network and + * belongs to a residential ISP. + * @param isTorExitNode Whether the IP address is a Tor exit node. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. */ -public class AnonymousIpResponse extends IpBaseResponse { +public record AnonymousIpResponse( + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, - /** - * Constructs an instance of {@code AnonymousIpResponse} with the specified values. - * - * @param ipAddress the IP address being checked - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - */ - public AnonymousIpResponse( - @JsonProperty("ip_address") String ipAddress, - @JsonProperty("is_anonymous") boolean isAnonymous, - @JsonProperty("is_anonymous_vpn") boolean isAnonymousVpn, - @JsonProperty("is_hosting_provider") boolean isHostingProvider, - @JsonProperty("is_public_proxy") boolean isPublicProxy, - @JsonProperty("is_residential_proxy") boolean isResidentialProxy, - @JsonProperty("is_tor_exit_node") boolean isTorExitNode, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) Network network - ) { - super(ipAddress, isAnonymous, isAnonymousVpn, isHostingProvider, isPublicProxy, - isResidentialProxy, isTorExitNode, network); - } + @JsonProperty("is_anonymous") + @MaxMindDbParameter(name = "is_anonymous") + boolean isAnonymous, + + @JsonProperty("is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn") + boolean isAnonymousVpn, + + @JsonProperty("is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider") + boolean isHostingProvider, + + @JsonProperty("is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy") + boolean isPublicProxy, + + @JsonProperty("is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy") + boolean isResidentialProxy, + + @JsonProperty("is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node") + boolean isTorExitNode, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network +) implements JsonSerializable { /** - * Constructs an instance of {@code AnonymousIpResponse} with the specified values. + * Constructs an instance of {@code AnonymousIpResponse} with nullable boolean fields. * * @param ipAddress the IP address being checked * @param isAnonymous whether the IP address belongs to any sort of anonymous network @@ -99,4 +122,27 @@ public AnonymousIpResponse( network ); } + + /** + * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("ip_address") + public String getIpAddress() { + return ipAddress(); + } + + /** + * @return The network associated with the record. In particular, this is + * the largest network where all the fields besides IP address have the + * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty + @JsonSerialize(using = ToStringSerializer.class) + public Network getNetwork() { + return network(); + } } diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java index 169d1e2a8..2a7a66162 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java @@ -3,59 +3,88 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; import java.time.LocalDate; /** * This class provides the GeoIP Anonymous Plus model. + * + * @param ipAddress The IP address that the data in the model is for. + * @param isAnonymous Whether the IP address belongs to any sort of anonymous network. + * @param isAnonymousVpn Whether the IP address is registered to an anonymous VPN provider. If a + * VPN provider does not register subnets under names associated with them, + * we will likely only flag their IP ranges using isHostingProvider. + * @param isHostingProvider Whether the IP address belongs to a hosting or VPN provider (see + * description of isAnonymousVpn). + * @param isPublicProxy Whether the IP address belongs to a public proxy. + * @param isResidentialProxy Whether the IP address is on a suspected anonymizing network and + * belongs to a residential ISP. + * @param isTorExitNode Whether the IP address is a Tor exit node. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. + * @param anonymizerConfidence A score ranging from 1 to 99 that is our percent confidence that + * the network is currently part of an actively used VPN service. + * @param networkLastSeen The last day that the network was sighted in our analysis of anonymized + * networks. + * @param providerName The name of the VPN provider (e.g., NordVPN, SurfShark, etc.) associated + * with the network. */ -public class AnonymousPlusResponse extends AnonymousIpResponse { - private final Integer anonymizerConfidence; - private final LocalDate networkLastSeen; - private final String providerName; +public record AnonymousPlusResponse( + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, - /** - * Constructs an instance of {@code AnonymousPlusResponse} with the specified values. - * - * @param anonymizerConfidence confidence that the network is a VPN. - * @param ipAddress the IP address being checked - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - * @param networkLastSeen the last sighting of the network. - * @param providerName the name of the VPN provider. - */ - public AnonymousPlusResponse( - @JsonProperty("anonymizer_confidence") Integer anonymizerConfidence, - @JsonProperty("ip_address") String ipAddress, - @JsonProperty("is_anonymous") Boolean isAnonymous, - @JsonProperty("is_anonymous_vpn") Boolean isAnonymousVpn, - @JsonProperty("is_hosting_provider") Boolean isHostingProvider, - @JsonProperty("is_public_proxy") Boolean isPublicProxy, - @JsonProperty("is_residential_proxy") Boolean isResidentialProxy, - @JsonProperty("is_tor_exit_node") Boolean isTorExitNode, - @JsonDeserialize(using = NetworkDeserializer.class) - @JsonProperty("network") Network network, - @JsonProperty("network_last_seen") LocalDate networkLastSeen, - @JsonProperty("provider_name") String providerName - ) { - super(ipAddress, isAnonymous, isAnonymousVpn, isHostingProvider, isPublicProxy, - isResidentialProxy, isTorExitNode, network); + @JsonProperty("is_anonymous") + @MaxMindDbParameter(name = "is_anonymous") + boolean isAnonymous, - this.anonymizerConfidence = anonymizerConfidence; - this.networkLastSeen = networkLastSeen; - this.providerName = providerName; - } + @JsonProperty("is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn") + boolean isAnonymousVpn, + + @JsonProperty("is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider") + boolean isHostingProvider, + + @JsonProperty("is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy") + boolean isPublicProxy, + + @JsonProperty("is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy") + boolean isResidentialProxy, + + @JsonProperty("is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node") + boolean isTorExitNode, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network, + + @JsonProperty("anonymizer_confidence") + @MaxMindDbParameter(name = "anonymizer_confidence") + Integer anonymizerConfidence, + + @JsonProperty("network_last_seen") + @MaxMindDbParameter(name = "network_last_seen") + LocalDate networkLastSeen, + + @JsonProperty("provider_name") + @MaxMindDbParameter(name = "provider_name") + String providerName +) implements JsonSerializable { /** - * Constructs an instance of {@code AnonymousPlusResponse} with the specified values. + * Constructs an instance of {@code AnonymousPlusResponse} with nullable boolean fields + * and date parsing from MaxMind database. * * @param anonymizerConfidence confidence that the network is a VPN. * @param ipAddress the IP address being checked @@ -83,10 +112,19 @@ public AnonymousPlusResponse( @MaxMindDbParameter(name = "network_last_seen") String networkLastSeen, @MaxMindDbParameter(name = "provider_name") String providerName ) { - this(anonymizerConfidence, ipAddress, isAnonymous, isAnonymousVpn, - isHostingProvider, isPublicProxy, isResidentialProxy, isTorExitNode, network, + this( + ipAddress, + isAnonymous != null ? isAnonymous : false, + isAnonymousVpn != null ? isAnonymousVpn : false, + isHostingProvider != null ? isHostingProvider : false, + isPublicProxy != null ? isPublicProxy : false, + isResidentialProxy != null ? isResidentialProxy : false, + isTorExitNode != null ? isTorExitNode : false, + network, + anonymizerConfidence, networkLastSeen != null ? LocalDate.parse(networkLastSeen) : null, - providerName); + providerName + ); } /** @@ -103,7 +141,6 @@ public AnonymousPlusResponse( Network network ) { this( - response.getAnonymizerConfidence(), ipAddress, response.isAnonymous(), response.isAnonymousVpn(), @@ -112,34 +149,65 @@ public AnonymousPlusResponse( response.isResidentialProxy(), response.isTorExitNode(), network, - response.getNetworkLastSeen(), - response.getProviderName() + response.anonymizerConfidence(), + response.networkLastSeen(), + response.providerName() ); } + /** + * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("ip_address") + public String getIpAddress() { + return ipAddress(); + } + + /** + * @return The network associated with the record. In particular, this is + * the largest network where all the fields besides IP address have the + * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty + @JsonSerialize(using = ToStringSerializer.class) + public Network getNetwork() { + return network(); + } + /** * @return A score ranging from 1 to 99 that is our percent confidence that the network is * currently part of an actively used VPN service. + * @deprecated Use {@link #anonymizerConfidence()} instead. This method will be removed + * in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty public Integer getAnonymizerConfidence() { - return anonymizerConfidence; + return anonymizerConfidence(); } /** * @return The last day that the network was sighted in our analysis of anonymized networks. + * @deprecated Use {@link #networkLastSeen()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty public LocalDate getNetworkLastSeen() { - return networkLastSeen; + return networkLastSeen(); } /** * @return The name of the VPN provider (e.g., NordVPN, SurfShark, etc.) associated with the * network. + * @deprecated Use {@link #providerName()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty public String getProviderName() { - return providerName; + return providerName(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java index a33717d26..56d22abcc 100644 --- a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java @@ -8,45 +8,43 @@ import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoLite2 ASN model. + * + * @param autonomousSystemNumber The autonomous system number associated with the IP address. + * @param autonomousSystemOrganization The organization associated with the registered autonomous + * system number for the IP address. + * @param ipAddress The IP address that the data in the model is for. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. */ -public class AsnResponse extends AbstractResponse { +public record AsnResponse( + @JsonProperty("autonomous_system_number") + @MaxMindDbParameter(name = "autonomous_system_number") + Long autonomousSystemNumber, + + @JsonProperty("autonomous_system_organization") + @MaxMindDbParameter(name = "autonomous_system_organization") + String autonomousSystemOrganization, + + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, - private final Long autonomousSystemNumber; - private final String autonomousSystemOrganization; - private final String ipAddress; - private final Network network; + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network +) implements JsonSerializable { /** - * Constructs an instance of {@code AsnResponse} with the specified values for all fields. - * - * @param autonomousSystemNumber the autonomous system number associated with the IP - * address - * @param autonomousSystemOrganization the organization associated with the registered - * autonomous system number for the IP address - * @param ipAddress the IP address that the data in the model is for - * @param network the network associated with the record + * Canonical constructor. */ @MaxMindDbConstructor - public AsnResponse( - @JsonProperty("autonomous_system_number") - @MaxMindDbParameter(name = "autonomous_system_number") Long autonomousSystemNumber, - @JsonProperty("autonomous_system_organization") - @MaxMindDbParameter(name = "autonomous_system_organization") - String autonomousSystemOrganization, - @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) @MaxMindDbParameter(name = "network") - Network network - ) { - this.autonomousSystemNumber = autonomousSystemNumber; - this.autonomousSystemOrganization = autonomousSystemOrganization; - this.ipAddress = ipAddress; - this.network = network; + public AsnResponse { } /** @@ -62,8 +60,8 @@ public AsnResponse( Network network ) { this( - response.getAutonomousSystemNumber(), - response.getAutonomousSystemOrganization(), + response.autonomousSystemNumber(), + response.autonomousSystemOrganization(), ipAddress, network ); @@ -71,37 +69,47 @@ public AsnResponse( /** * @return The autonomous system number associated with the IP address. + * @deprecated Use {@link #autonomousSystemNumber()} instead. This method will be removed + * in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("autonomous_system_number") public Long getAutonomousSystemNumber() { - return this.autonomousSystemNumber; + return autonomousSystemNumber(); } /** * @return The organization associated with the registered autonomous system * number for the IP address + * @deprecated Use {@link #autonomousSystemOrganization()} instead. This method will be + * removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("autonomous_system_organization") public String getAutonomousSystemOrganization() { - return this.autonomousSystemOrganization; + return autonomousSystemOrganization(); } /** * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return this.ipAddress; + return ipAddress(); } /** * @return The network associated with the record. In particular, this is * the largest network where all the fields besides IP address have the * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty @JsonSerialize(using = ToStringSerializer.class) public Network getNetwork() { - return this.network; + return network(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/CityResponse.java b/src/main/java/com/maxmind/geoip2/model/CityResponse.java index a9e64f65c..bbc83552b 100644 --- a/src/main/java/com/maxmind/geoip2/model/CityResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CityResponse.java @@ -1,10 +1,12 @@ package com.maxmind.geoip2.model; import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; @@ -21,43 +23,87 @@ * This class provides a model for the data returned by the City Plus web * service and the City database. * + * @param city City record for the requested IP address. + * @param continent Continent record for the requested IP address. + * @param country Country record for the requested IP address. This object represents the country + * where MaxMind believes the end user is located. + * @param location Location record for the requested IP address. + * @param maxmind MaxMind record containing data related to your account. + * @param postal Postal record for the requested IP address. + * @param registeredCountry Registered country record for the requested IP address. This record + * represents the country where the ISP has registered a given IP block + * and may differ from the user's country. + * @param representedCountry Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the country. + * @param subdivisions An {@link List} of {@link Subdivision} objects representing the country + * subdivisions for the requested IP address. The number and type of + * subdivisions varies by country, but a subdivision is typically a state, + * province, county, etc. Subdivisions are ordered from most general (largest) + * to most specific (smallest). If the response did not contain any + * subdivisions, this is an empty list. + * @param traits Record for the traits of the requested IP address. * @see GeoIP2 Web * Services */ -public final class CityResponse extends AbstractCityResponse { +public record CityResponse( + @JsonProperty("city") + @MaxMindDbParameter(name = "city") + City city, + + @JsonProperty("continent") + @MaxMindDbParameter(name = "continent") + Continent continent, + + @JsonProperty("country") + @MaxMindDbParameter(name = "country") + Country country, + + @JsonProperty("location") + @MaxMindDbParameter(name = "location") + Location location, + + @JsonProperty("maxmind") + @MaxMindDbParameter(name = "maxmind") + MaxMind maxmind, + + @JsonProperty("postal") + @MaxMindDbParameter(name = "postal") + Postal postal, + + @JsonProperty("registered_country") + @MaxMindDbParameter(name = "registered_country") + Country registeredCountry, + + @JsonProperty("represented_country") + @MaxMindDbParameter(name = "represented_country") + RepresentedCountry representedCountry, + + @JsonProperty("subdivisions") + @MaxMindDbParameter(name = "subdivisions") + List subdivisions, + + @JsonProperty("traits") + @MaxMindDbParameter(name = "traits") + Traits traits +) implements JsonSerializable { + /** - * Constructs an instance of {@code CityResponse} with the specified parameters. - * - * @param city city - * @param continent continent - * @param country country - * @param location location - * @param maxmind maxmind record for the response - * @param postal postal - * @param registeredCountry registered country - * @param representedCountry represented country - * @param subdivisions subdivisions - * @param traits traits + * Compact canonical constructor that sets defaults for null values. */ @MaxMindDbConstructor - public CityResponse( - @JsonProperty("city") @MaxMindDbParameter(name = "city") City city, - @JsonProperty("continent") @MaxMindDbParameter(name = "continent") Continent continent, - @JsonProperty("country") @MaxMindDbParameter(name = "country") Country country, - @JsonProperty("location") @MaxMindDbParameter(name = "location") Location location, - @JsonProperty("maxmind") @MaxMindDbParameter(name = "maxmind") MaxMind maxmind, - @JsonProperty("postal") @MaxMindDbParameter(name = "postal") Postal postal, - @JsonProperty("registered_country") @MaxMindDbParameter(name = "registered_country") - Country registeredCountry, - @JsonProperty("represented_country") @MaxMindDbParameter(name = "represented_country") - RepresentedCountry representedCountry, - @JsonProperty("subdivisions") @MaxMindDbParameter(name = "subdivisions") - ArrayList subdivisions, - @JsonProperty("traits") @MaxMindDbParameter(name = "traits") - Traits traits - ) { - super(city, continent, country, location, maxmind, postal, registeredCountry, - representedCountry, subdivisions, traits); + public CityResponse { + city = city != null ? city : new City(); + continent = continent != null ? continent : new Continent(); + country = country != null ? country : new Country(); + location = location != null ? location : new Location(); + maxmind = maxmind != null ? maxmind : new MaxMind(); + postal = postal != null ? postal : new Postal(); + registeredCountry = registeredCountry != null ? registeredCountry : new Country(); + representedCountry = representedCountry != null + ? representedCountry : new RepresentedCountry(); + subdivisions = subdivisions != null ? List.copyOf(subdivisions) : List.of(); + traits = traits != null ? traits : new Traits(); } /** @@ -74,6 +120,186 @@ public CityResponse( Network network, List locales ) { - super(response, ipAddress, network, locales); + this( + new City(response.city(), locales), + new Continent(response.continent(), locales), + new Country(response.country(), locales), + response.location(), + response.maxmind(), + response.postal(), + new Country(response.registeredCountry(), locales), + new RepresentedCountry(response.representedCountry(), locales), + mapSubdivisions(response.subdivisions(), locales), + new Traits(response.traits(), ipAddress, network) + ); + } + + private static ArrayList mapSubdivisions( + List subdivisions, + List locales + ) { + ArrayList subdivisions2 = new ArrayList<>(subdivisions.size()); + for (Subdivision subdivision : subdivisions) { + subdivisions2.add(new Subdivision(subdivision, locales)); + } + return subdivisions2; + } + + /** + * @return City record for the requested IP address. + * @deprecated Use {@link #city()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public City getCity() { + return city(); + } + + /** + * @return Continent record for the requested IP address. + * @deprecated Use {@link #continent()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Continent getContinent() { + return continent(); + } + + /** + * @return Country record for the requested IP address. This object + * represents the country where MaxMind believes the end user is + * located. + * @deprecated Use {@link #country()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Country getCountry() { + return country(); + } + + /** + * @return Location record for the requested IP address. + * @deprecated Use {@link #location()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Location getLocation() { + return location(); + } + + /** + * @return MaxMind record containing data related to your account. + * @deprecated Use {@link #maxmind()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("maxmind") + public MaxMind getMaxMind() { + return maxmind(); + } + + /** + * @return the postal + * @deprecated Use {@link #postal()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Postal getPostal() { + return postal(); + } + + /** + * @return Registered country record for the requested IP address. This + * record represents the country where the ISP has registered a + * given IP block and may differ from the user's country. + * @deprecated Use {@link #registeredCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("registered_country") + public Country getRegisteredCountry() { + return registeredCountry(); + } + + /** + * @return Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the + * country. + * @deprecated Use {@link #representedCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("represented_country") + public RepresentedCountry getRepresentedCountry() { + return representedCountry(); + } + + /** + * @return An {@link List} of {@link Subdivision} objects representing the + * country subdivisions for the requested IP address. The number and + * type of subdivisions varies by country, but a subdivision is + * typically a state, province, county, etc. Subdivisions are + * ordered from most general (largest) to most specific (smallest). + * If the response did not contain any subdivisions, this method + * returns an empty array. + * @deprecated Use {@link #subdivisions()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public List getSubdivisions() { + return new ArrayList<>(subdivisions()); + } + + /** + * @return Record for the traits of the requested IP address. + * @deprecated Use {@link #traits()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Traits getTraits() { + return traits(); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision mostSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(subdivisions().size() - 1); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #mostSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getMostSpecificSubdivision() { + return mostSpecificSubdivision(); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision leastSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(0); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #leastSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getLeastSpecificSubdivision() { + return leastSpecificSubdivision(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java index 06c956f75..44d02aa85 100644 --- a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java @@ -10,12 +10,31 @@ import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoIP2 Connection-Type model. + * + * @param connectionType The connection type of the IP address. + * @param ipAddress The IP address that the data in the model is for. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. */ -public class ConnectionTypeResponse extends AbstractResponse { +public record ConnectionTypeResponse( + @JsonProperty("connection_type") + @MaxMindDbParameter(name = "connection_type") + ConnectionType connectionType, + + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network +) implements JsonSerializable { /** * The enumerated values that connection-type may take. @@ -66,35 +85,14 @@ public static ConnectionType fromString(String s) { } } - private final ConnectionType connectionType; - private final String ipAddress; - private final Network network; - /** - * Constructs an instance of {@code ConnectionTypeResponse}. + * Constructs an instance of {@code ConnectionTypeResponse} from MaxMind database + * with String-to-enum conversion. * - * @param connectionType The connection type of the IP address. + * @param connectionType The connection type of the IP address as a string. * @param ipAddress The IP address that the data in the model is for. * @param network The network associated with the record. */ - public ConnectionTypeResponse( - @JsonProperty("connection_type") ConnectionType connectionType, - @JsonProperty("ip_address") String ipAddress, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) Network network - ) { - this.connectionType = connectionType; - this.ipAddress = ipAddress; - this.network = network; - } - - /** - * Constructs an instance of {@code ConnectionTypeResponse}. - * - * @param connectionType The connection type of the IP address. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ @MaxMindDbConstructor public ConnectionTypeResponse( @MaxMindDbParameter(name = "connection_type") String connectionType, @@ -113,7 +111,7 @@ public ConnectionTypeResponse( * * @param response The {@code ConnectionTypeResponse} object to copy. * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. + * @param network The network associated with the record. */ public ConnectionTypeResponse( ConnectionTypeResponse response, @@ -121,7 +119,7 @@ public ConnectionTypeResponse( Network network ) { this( - response.getConnectionType(), + response.connectionType(), ipAddress, network ); @@ -129,28 +127,34 @@ public ConnectionTypeResponse( /** * @return The connection type of the IP address. + * @deprecated Use {@link #connectionType()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("connection_type") public ConnectionType getConnectionType() { - return this.connectionType; + return connectionType(); } /** * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return this.ipAddress; + return ipAddress(); } /** * @return The network associated with the record. In particular, this is * the largest network where all the fields besides IP address have the * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty @JsonSerialize(using = ToStringSerializer.class) public Network getNetwork() { - return this.network; + return network(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java index 364c9bc60..eb19ec901 100644 --- a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java @@ -5,6 +5,7 @@ import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; import com.maxmind.geoip2.record.MaxMind; @@ -16,34 +17,58 @@ * This class provides a model for the data returned by the Country web service * and the Country database. * + * @param continent Continent record for the requested IP address. + * @param country Country record for the requested IP address. This object represents the country + * where MaxMind believes the end user is located. + * @param maxmind MaxMind record containing data related to your account. + * @param registeredCountry Registered country record for the requested IP address. This record + * represents the country where the ISP has registered a given IP block + * and may differ from the user's country. + * @param representedCountry Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the country. + * @param traits Record for the traits of the requested IP address. * @see GeoIP2 Web * Services */ -public final class CountryResponse extends AbstractCountryResponse { +public record CountryResponse( + @JsonProperty("continent") + @MaxMindDbParameter(name = "continent") + Continent continent, + + @JsonProperty("country") + @MaxMindDbParameter(name = "country") + Country country, + + @JsonProperty("maxmind") + @MaxMindDbParameter(name = "maxmind") + MaxMind maxmind, + + @JsonProperty("registered_country") + @MaxMindDbParameter(name = "registered_country") + Country registeredCountry, + + @JsonProperty("represented_country") + @MaxMindDbParameter(name = "represented_country") + RepresentedCountry representedCountry, + + @JsonProperty("traits") + @MaxMindDbParameter(name = "traits") + Traits traits +) implements JsonSerializable { /** - * Constructs an instance of {@code CountryResponse} with the specified parameters. - * - * @param continent the continent - * @param country the country - * @param maxmind the MaxMind record - * @param registeredCountry the registered country - * @param representedCountry the represented country - * @param traits the traits + * Compact canonical constructor that sets defaults for null values. */ @MaxMindDbConstructor - public CountryResponse( - @JsonProperty("continent") @MaxMindDbParameter(name = "continent") Continent continent, - @JsonProperty("country") @MaxMindDbParameter(name = "country") Country country, - @JsonProperty("maxmind") @MaxMindDbParameter(name = "maxmind") MaxMind maxmind, - @JsonProperty("registered_country") @MaxMindDbParameter(name = "registered_country") - Country registeredCountry, - @JsonProperty("represented_country") @MaxMindDbParameter(name = "represented_country") - RepresentedCountry representedCountry, - @JsonProperty("traits") @MaxMindDbParameter(name = "traits") - Traits traits - ) { - super(continent, country, maxmind, registeredCountry, representedCountry, traits); + public CountryResponse { + continent = continent != null ? continent : new Continent(); + country = country != null ? country : new Country(); + maxmind = maxmind != null ? maxmind : new MaxMind(); + registeredCountry = registeredCountry != null ? registeredCountry : new Country(); + representedCountry = representedCountry != null + ? representedCountry : new RepresentedCountry(); + traits = traits != null ? traits : new Traits(); } /** @@ -60,6 +85,77 @@ public CountryResponse( Network network, List locales ) { - super(response, ipAddress, network, locales); + this( + new Continent(response.continent(), locales), + new Country(response.country(), locales), + response.maxmind(), + new Country(response.registeredCountry(), locales), + new RepresentedCountry(response.representedCountry(), locales), + new Traits(response.traits(), ipAddress, network) + ); + } + + /** + * @return MaxMind record containing data related to your account. + * @deprecated Use {@link #maxmind()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("maxmind") + public MaxMind getMaxMind() { + return maxmind(); + } + + /** + * @return Registered country record for the requested IP address. This + * record represents the country where the ISP has registered a + * given IP block and may differ from the user's country. + * @deprecated Use {@link #registeredCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("registered_country") + public Country getRegisteredCountry() { + return registeredCountry(); + } + + /** + * @return Continent record for the requested IP address. + * @deprecated Use {@link #continent()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Continent getContinent() { + return continent(); + } + + /** + * @return Country record for the requested IP address. This object + * represents the country where MaxMind believes the end user is + * located. + * @deprecated Use {@link #country()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Country getCountry() { + return country(); + } + + /** + * @return Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the + * country. + * @deprecated Use {@link #representedCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("represented_country") + public RepresentedCountry getRepresentedCountry() { + return representedCountry(); + } + + /** + * @return Record for the traits of the requested IP address. + * @deprecated Use {@link #traits()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Traits getTraits() { + return traits(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java index 81b12987d..97b994994 100644 --- a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java @@ -8,40 +8,42 @@ import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoIP2 Domain model. + * + * @param domain The second level domain associated with the IP address. This will be something + * like "example.com" or "example.co.uk", not "foo.example.com". + * @param ipAddress The IP address that the data in the model is for. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. */ -public class DomainResponse extends AbstractResponse { +public record DomainResponse( + @JsonProperty("domain") + @MaxMindDbParameter(name = "domain") + String domain, - private final String domain; - private final String ipAddress; - private final Network network; + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network +) implements JsonSerializable { /** - * Constructs an instance of {@code DomainResponse}. - * - * @param domain the second level domain associated with the IP address - * @param ipAddress the IP address that the data in the model is for - * @param network the network associated with the record + * Canonical constructor. */ @MaxMindDbConstructor - public DomainResponse( - @JsonProperty("domain") @MaxMindDbParameter(name = "domain") String domain, - @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) @MaxMindDbParameter(name = "network") - Network network - ) { - this.domain = domain; - this.ipAddress = ipAddress; - this.network = network; + public DomainResponse { } /** - * Constructs an instance of {@code DomainResponse} with only required parameters. + * Constructs an instance of {@code DomainResponse} with only required parameters. * * @param response the response * @param ipAddress the IP address that the data in the model is for. @@ -52,34 +54,40 @@ public DomainResponse( String ipAddress, Network network ) { - this(response.getDomain(), ipAddress, network); + this(response.domain(), ipAddress, network); } /** * @return The second level domain associated with the IP address. This * will be something like "example.com" or "example.co.uk", not * "foo.example.com". + * @deprecated Use {@link #domain()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getDomain() { - return this.domain; + return domain(); } /** * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return this.ipAddress; + return ipAddress(); } /** * @return The network associated with the record. In particular, this is * the largest network where all the fields besides IP address have the * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty @JsonSerialize(using = ToStringSerializer.class) public Network getNetwork() { - return this.network; + return network(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java index 700c025f7..4322e7fe8 100644 --- a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java @@ -1,10 +1,12 @@ package com.maxmind.geoip2.model; import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; @@ -22,42 +24,86 @@ * This class provides a model for the data returned by the GeoIP2 Enterprise * database *

+ * + * @param city City record for the requested IP address. + * @param continent Continent record for the requested IP address. + * @param country Country record for the requested IP address. This object represents the country + * where MaxMind believes the end user is located. + * @param location Location record for the requested IP address. + * @param maxmind MaxMind record containing data related to your account. + * @param postal Postal record for the requested IP address. + * @param registeredCountry Registered country record for the requested IP address. This record + * represents the country where the ISP has registered a given IP block + * and may differ from the user's country. + * @param representedCountry Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the country. + * @param subdivisions An {@link List} of {@link Subdivision} objects representing the country + * subdivisions for the requested IP address. The number and type of + * subdivisions varies by country, but a subdivision is typically a state, + * province, county, etc. Subdivisions are ordered from most general (largest) + * to most specific (smallest). If the response did not contain any + * subdivisions, this is an empty list. + * @param traits Record for the traits of the requested IP address. */ -public final class EnterpriseResponse extends AbstractCityResponse { +public record EnterpriseResponse( + @JsonProperty("city") + @MaxMindDbParameter(name = "city") + City city, + + @JsonProperty("continent") + @MaxMindDbParameter(name = "continent") + Continent continent, + + @JsonProperty("country") + @MaxMindDbParameter(name = "country") + Country country, + + @JsonProperty("location") + @MaxMindDbParameter(name = "location") + Location location, + + @JsonProperty("maxmind") + @MaxMindDbParameter(name = "maxmind") + MaxMind maxmind, + + @JsonProperty("postal") + @MaxMindDbParameter(name = "postal") + Postal postal, + + @JsonProperty("registered_country") + @MaxMindDbParameter(name = "registered_country") + Country registeredCountry, + + @JsonProperty("represented_country") + @MaxMindDbParameter(name = "represented_country") + RepresentedCountry representedCountry, + + @JsonProperty("subdivisions") + @MaxMindDbParameter(name = "subdivisions") + List subdivisions, + + @JsonProperty("traits") + @MaxMindDbParameter(name = "traits") + Traits traits +) implements JsonSerializable { /** - * Constructs an instance of {@code EnterpriseResponse} with the specified parameters. - * - * @param city city - * @param continent continent - * @param country country - * @param location location - * @param maxmind maxmind record for the response - * @param postal postal - * @param registeredCountry registered country - * @param representedCountry represented country - * @param subdivisions subdivisions - * @param traits traits + * Compact canonical constructor that sets defaults for null values. */ @MaxMindDbConstructor - public EnterpriseResponse( - @JsonProperty("city") @MaxMindDbParameter(name = "city") City city, - @JsonProperty("continent") @MaxMindDbParameter(name = "continent") Continent continent, - @JsonProperty("country") @MaxMindDbParameter(name = "country") Country country, - @JsonProperty("location") @MaxMindDbParameter(name = "location") Location location, - @JsonProperty("maxmind") @MaxMindDbParameter(name = "maxmind") MaxMind maxmind, - @JsonProperty("postal") @MaxMindDbParameter(name = "postal") Postal postal, - @JsonProperty("registered_country") @MaxMindDbParameter(name = "registered_country") - Country registeredCountry, - @JsonProperty("represented_country") @MaxMindDbParameter(name = "represented_country") - RepresentedCountry representedCountry, - @JsonProperty("subdivisions") @MaxMindDbParameter(name = "subdivisions") - ArrayList subdivisions, - @JsonProperty("traits") @MaxMindDbParameter(name = "traits") - Traits traits - ) { - super(city, continent, country, location, maxmind, postal, registeredCountry, - representedCountry, subdivisions, traits); + public EnterpriseResponse { + city = city != null ? city : new City(); + continent = continent != null ? continent : new Continent(); + country = country != null ? country : new Country(); + location = location != null ? location : new Location(); + maxmind = maxmind != null ? maxmind : new MaxMind(); + postal = postal != null ? postal : new Postal(); + registeredCountry = registeredCountry != null ? registeredCountry : new Country(); + representedCountry = representedCountry != null + ? representedCountry : new RepresentedCountry(); + subdivisions = subdivisions != null ? List.copyOf(subdivisions) : List.of(); + traits = traits != null ? traits : new Traits(); } /** @@ -74,6 +120,186 @@ public EnterpriseResponse( Network network, List locales ) { - super(response, ipAddress, network, locales); + this( + new City(response.city(), locales), + new Continent(response.continent(), locales), + new Country(response.country(), locales), + response.location(), + response.maxmind(), + response.postal(), + new Country(response.registeredCountry(), locales), + new RepresentedCountry(response.representedCountry(), locales), + mapSubdivisions(response.subdivisions(), locales), + new Traits(response.traits(), ipAddress, network) + ); + } + + private static ArrayList mapSubdivisions( + List subdivisions, + List locales + ) { + ArrayList subdivisions2 = new ArrayList<>(subdivisions.size()); + for (Subdivision subdivision : subdivisions) { + subdivisions2.add(new Subdivision(subdivision, locales)); + } + return subdivisions2; + } + + /** + * @return City record for the requested IP address. + * @deprecated Use {@link #city()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public City getCity() { + return city(); + } + + /** + * @return Continent record for the requested IP address. + * @deprecated Use {@link #continent()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Continent getContinent() { + return continent(); + } + + /** + * @return Country record for the requested IP address. This object + * represents the country where MaxMind believes the end user is + * located. + * @deprecated Use {@link #country()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Country getCountry() { + return country(); + } + + /** + * @return Location record for the requested IP address. + * @deprecated Use {@link #location()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Location getLocation() { + return location(); + } + + /** + * @return MaxMind record containing data related to your account. + * @deprecated Use {@link #maxmind()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("maxmind") + public MaxMind getMaxMind() { + return maxmind(); + } + + /** + * @return the postal + * @deprecated Use {@link #postal()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Postal getPostal() { + return postal(); + } + + /** + * @return Registered country record for the requested IP address. This + * record represents the country where the ISP has registered a + * given IP block and may differ from the user's country. + * @deprecated Use {@link #registeredCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("registered_country") + public Country getRegisteredCountry() { + return registeredCountry(); + } + + /** + * @return Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the + * country. + * @deprecated Use {@link #representedCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("represented_country") + public RepresentedCountry getRepresentedCountry() { + return representedCountry(); + } + + /** + * @return An {@link List} of {@link Subdivision} objects representing the + * country subdivisions for the requested IP address. The number and + * type of subdivisions varies by country, but a subdivision is + * typically a state, province, county, etc. Subdivisions are + * ordered from most general (largest) to most specific (smallest). + * If the response did not contain any subdivisions, this method + * returns an empty array. + * @deprecated Use {@link #subdivisions()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public List getSubdivisions() { + return new ArrayList<>(subdivisions()); + } + + /** + * @return Record for the traits of the requested IP address. + * @deprecated Use {@link #traits()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Traits getTraits() { + return traits(); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision mostSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(subdivisions().size() - 1); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #mostSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getMostSpecificSubdivision() { + return mostSpecificSubdivision(); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision leastSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(0); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #leastSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getLeastSpecificSubdivision() { + return leastSpecificSubdivision(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java b/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java index b96bceb7a..7196c5f92 100644 --- a/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java @@ -1,7 +1,9 @@ package com.maxmind.geoip2.model; import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; @@ -11,43 +13,240 @@ import com.maxmind.geoip2.record.RepresentedCountry; import com.maxmind.geoip2.record.Subdivision; import com.maxmind.geoip2.record.Traits; +import java.util.ArrayList; import java.util.List; /** * This class provides a model for the data returned by the Insights web * service. * + * @param city City record for the requested IP address. + * @param continent Continent record for the requested IP address. + * @param country Country record for the requested IP address. This object represents the country + * where MaxMind believes the end user is located. + * @param location Location record for the requested IP address. + * @param maxmind MaxMind record containing data related to your account. + * @param postal Postal record for the requested IP address. + * @param registeredCountry Registered country record for the requested IP address. This record + * represents the country where the ISP has registered a given IP block + * and may differ from the user's country. + * @param representedCountry Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the country. + * @param subdivisions An {@link List} of {@link Subdivision} objects representing the country + * subdivisions for the requested IP address. The number and type of + * subdivisions varies by country, but a subdivision is typically a state, + * province, county, etc. Subdivisions are ordered from most general (largest) + * to most specific (smallest). If the response did not contain any + * subdivisions, this is an empty list. + * @param traits Record for the traits of the requested IP address. * @see GeoIP2 Web * Services */ -public class InsightsResponse extends AbstractCityResponse { - /** - * Constructs an instance of {@code InsightsResponse} with the specified parameters. - * - * @param city city - * @param continent continent - * @param country country - * @param location location - * @param maxmind maxmind record for the response - * @param postal postal - * @param registeredCountry registered country - * @param representedCountry represented country - * @param subdivisions subdivisions - * @param traits traits - */ - public InsightsResponse( - @JsonProperty("city") City city, - @JsonProperty("continent") Continent continent, - @JsonProperty("country") Country country, - @JsonProperty("location") Location location, - @JsonProperty("maxmind") MaxMind maxmind, - @JsonProperty("postal") Postal postal, - @JsonProperty("registered_country") Country registeredCountry, - @JsonProperty("represented_country") RepresentedCountry representedCountry, - @JsonProperty("subdivisions") List subdivisions, - @JsonProperty("traits") Traits traits - ) { - super(city, continent, country, location, maxmind, postal, registeredCountry, - representedCountry, subdivisions, traits); +public record InsightsResponse( + @JsonProperty("city") + City city, + + @JsonProperty("continent") + Continent continent, + + @JsonProperty("country") + Country country, + + @JsonProperty("location") + Location location, + + @JsonProperty("maxmind") + MaxMind maxmind, + + @JsonProperty("postal") + Postal postal, + + @JsonProperty("registered_country") + Country registeredCountry, + + @JsonProperty("represented_country") + RepresentedCountry representedCountry, + + @JsonProperty("subdivisions") + List subdivisions, + + @JsonProperty("traits") + Traits traits +) implements JsonSerializable { + + /** + * Compact canonical constructor that sets defaults for null values. + */ + public InsightsResponse { + city = city != null ? city : new City(); + continent = continent != null ? continent : new Continent(); + country = country != null ? country : new Country(); + location = location != null ? location : new Location(); + maxmind = maxmind != null ? maxmind : new MaxMind(); + postal = postal != null ? postal : new Postal(); + registeredCountry = registeredCountry != null ? registeredCountry : new Country(); + representedCountry = representedCountry != null + ? representedCountry : new RepresentedCountry(); + subdivisions = subdivisions != null ? List.copyOf(subdivisions) : List.of(); + traits = traits != null ? traits : new Traits(); + } + + /** + * @return City record for the requested IP address. + * @deprecated Use {@link #city()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public City getCity() { + return city(); + } + + /** + * @return Continent record for the requested IP address. + * @deprecated Use {@link #continent()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Continent getContinent() { + return continent(); + } + + /** + * @return Country record for the requested IP address. This object + * represents the country where MaxMind believes the end user is + * located. + * @deprecated Use {@link #country()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Country getCountry() { + return country(); + } + + /** + * @return Location record for the requested IP address. + * @deprecated Use {@link #location()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Location getLocation() { + return location(); + } + + /** + * @return MaxMind record containing data related to your account. + * @deprecated Use {@link #maxmind()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("maxmind") + public MaxMind getMaxMind() { + return maxmind(); + } + + /** + * @return the postal + * @deprecated Use {@link #postal()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Postal getPostal() { + return postal(); + } + + /** + * @return Registered country record for the requested IP address. This + * record represents the country where the ISP has registered a + * given IP block and may differ from the user's country. + * @deprecated Use {@link #registeredCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("registered_country") + public Country getRegisteredCountry() { + return registeredCountry(); + } + + /** + * @return Represented country record for the requested IP address. The + * represented country is used for things like military bases. It is + * only present when the represented country differs from the + * country. + * @deprecated Use {@link #representedCountry()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("represented_country") + public RepresentedCountry getRepresentedCountry() { + return representedCountry(); + } + + /** + * @return An {@link List} of {@link Subdivision} objects representing the + * country subdivisions for the requested IP address. The number and + * type of subdivisions varies by country, but a subdivision is + * typically a state, province, county, etc. Subdivisions are + * ordered from most general (largest) to most specific (smallest). + * If the response did not contain any subdivisions, this method + * returns an empty array. + * @deprecated Use {@link #subdivisions()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public List getSubdivisions() { + return new ArrayList<>(subdivisions()); + } + + /** + * @return Record for the traits of the requested IP address. + * @deprecated Use {@link #traits()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Traits getTraits() { + return traits(); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision mostSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(subdivisions().size() - 1); + } + + /** + * @return An object representing the most specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #mostSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getMostSpecificSubdivision() { + return mostSpecificSubdivision(); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + */ + @JsonIgnore + public Subdivision leastSpecificSubdivision() { + if (subdivisions().isEmpty()) { + return new Subdivision(); + } + return subdivisions().get(0); + } + + /** + * @return An object representing the least specific subdivision returned. If + * the response did not contain any subdivisions, this method + * returns an empty {@link Subdivision} object. + * @deprecated Use {@link #leastSpecificSubdivision()} instead. This method will be removed + * in 6.0.0. + */ + @JsonIgnore + @Deprecated(since = "5.0.0", forRemoval = true) + public Subdivision getLeastSpecificSubdivision() { + return leastSpecificSubdivision(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/IpBaseResponse.java b/src/main/java/com/maxmind/geoip2/model/IpBaseResponse.java deleted file mode 100644 index fa3f65d33..000000000 --- a/src/main/java/com/maxmind/geoip2/model/IpBaseResponse.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.maxmind.geoip2.model; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.Network; - -/** - * This class provides the base IP model. - */ -public class IpBaseResponse extends AbstractResponse { - - private final boolean isAnonymous; - private final boolean isAnonymousVpn; - private final boolean isHostingProvider; - private final boolean isPublicProxy; - private final boolean isResidentialProxy; - private final boolean isTorExitNode; - private final String ipAddress; - private final Network network; - - /** - * Constructs an instance of {@code IpBaseResponse}. - * - * @param ipAddress the IP address that the data in the model is for - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - */ - public IpBaseResponse( - String ipAddress, - boolean isAnonymous, - boolean isAnonymousVpn, - boolean isHostingProvider, - boolean isPublicProxy, - boolean isResidentialProxy, - boolean isTorExitNode, - Network network - ) { - this.isAnonymous = isAnonymous; - this.isAnonymousVpn = isAnonymousVpn; - this.isHostingProvider = isHostingProvider; - this.isPublicProxy = isPublicProxy; - this.isResidentialProxy = isResidentialProxy; - this.isTorExitNode = isTorExitNode; - this.ipAddress = ipAddress; - this.network = network; - } - - /** - * @return whether the IP address belongs to any sort of anonymous network. - */ - @JsonProperty("is_anonymous") - public boolean isAnonymous() { - return isAnonymous; - } - - /** - * @return whether the IP address is registered to an anonymous VPN - * provider. If a VPN provider does not register subnets under names - * associated with them, we will likely only flag their IP ranges using - * isHostingProvider. - */ - @JsonProperty("is_anonymous_vpn") - public boolean isAnonymousVpn() { - return isAnonymousVpn; - } - - /** - * @return whether the IP address belongs to a hosting or VPN provider - * (see description of isAnonymousVpn). - */ - @JsonProperty("is_hosting_provider") - public boolean isHostingProvider() { - return isHostingProvider; - } - - /** - * @return whether the IP address belongs to a public proxy. - */ - @JsonProperty("is_public_proxy") - public boolean isPublicProxy() { - return isPublicProxy; - } - - /** - * @return whether the IP address is on a suspected anonymizing network and - * belongs to a residential ISP. - */ - @JsonProperty("is_residential_proxy") - public boolean isResidentialProxy() { - return isResidentialProxy; - } - - /** - * @return whether the IP address is a Tor exit node. - */ - @JsonProperty("is_tor_exit_node") - public boolean isTorExitNode() { - return isTorExitNode; - } - - /** - * @return The IP address that the data in the model is for. - */ - @JsonProperty("ip_address") - public String getIpAddress() { - return this.ipAddress; - } - - /** - * @return The network associated with the record. In particular, this is - * the largest network where all the fields besides IP address have the - * same value. - */ - @JsonProperty - @JsonSerialize(using = ToStringSerializer.class) - public Network getNetwork() { - return this.network; - } -} diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index 4d66c6942..f801ca930 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -3,51 +3,73 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoIP2 IP Risk model. * + * @param ipAddress The IP address that the data in the model is for. + * @param isAnonymous Whether the IP address belongs to any sort of anonymous network. + * @param isAnonymousVpn Whether the IP address is registered to an anonymous VPN provider. If a + * VPN provider does not register subnets under names associated with them, + * we will likely only flag their IP ranges using isHostingProvider. + * @param isHostingProvider Whether the IP address belongs to a hosting or VPN provider (see + * description of isAnonymousVpn). + * @param isPublicProxy Whether the IP address belongs to a public proxy. + * @param isResidentialProxy Whether the IP address is on a suspected anonymizing network and + * belongs to a residential ISP. + * @param isTorExitNode Whether the IP address is a Tor exit node. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. + * @param ipRisk The IP risk of a model. */ -public class IpRiskResponse extends IpBaseResponse { +public record IpRiskResponse( + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, - private final double ipRisk; + @JsonProperty("is_anonymous") + @MaxMindDbParameter(name = "is_anonymous") + boolean isAnonymous, - /** - * Constructs an instance of {@code IpRiskResponse}. - * - * @param ipAddress the IP address being checked - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - * @param ipRisk the IP risk of a model - */ - public IpRiskResponse( - @JsonProperty("ip_address") String ipAddress, - @JsonProperty("is_anonymous") boolean isAnonymous, - @JsonProperty("is_anonymous_vpn") boolean isAnonymousVpn, - @JsonProperty("is_hosting_provider") boolean isHostingProvider, - @JsonProperty("is_public_proxy") boolean isPublicProxy, - @JsonProperty("is_residential_proxy") boolean isResidentialProxy, - @JsonProperty("is_tor_exit_node") boolean isTorExitNode, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) Network network, - @JsonProperty("ip_risk") double ipRisk - ) { - super(ipAddress, isAnonymous, isAnonymousVpn, isHostingProvider, isPublicProxy, - isResidentialProxy, isTorExitNode, network); - this.ipRisk = ipRisk; - } + @JsonProperty("is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn") + boolean isAnonymousVpn, + + @JsonProperty("is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider") + boolean isHostingProvider, + + @JsonProperty("is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy") + boolean isPublicProxy, + + @JsonProperty("is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy") + boolean isResidentialProxy, + + @JsonProperty("is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node") + boolean isTorExitNode, + + @JsonProperty("network") + @MaxMindDbParameter(name = "network") + @JsonDeserialize(using = NetworkDeserializer.class) + Network network, + + @JsonProperty("ip_risk") + @MaxMindDbParameter(name = "ip_risk") + double ipRisk +) implements JsonSerializable { /** - * Constructs an instance of {@code IpRiskResponse}. + * Constructs an instance of {@code IpRiskResponse} with nullable boolean fields. * * @param ipAddress the IP address being checked * @param isAnonymous whether the IP address belongs to any sort of anonymous network @@ -58,7 +80,6 @@ public IpRiskResponse( * @param isTorExitNode whether the IP address is a Tor exit node * @param network the network associated with the record * @param ipRisk the IP risk of a model - * */ @MaxMindDbConstructor public IpRiskResponse( @@ -71,7 +92,6 @@ public IpRiskResponse( @MaxMindDbParameter(name = "is_tor_exit_node") Boolean isTorExitNode, @MaxMindDbParameter(name = "network") Network network, @MaxMindDbParameter(name = "ip_risk") double ipRisk - ) { this( ipAddress, @@ -83,7 +103,6 @@ public IpRiskResponse( isTorExitNode != null ? isTorExitNode : false, network, ipRisk - ); } @@ -108,15 +127,40 @@ public IpRiskResponse( response.isResidentialProxy(), response.isTorExitNode(), network, - response.ipRisk + response.ipRisk() ); } + /** + * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("ip_address") + public String getIpAddress() { + return ipAddress(); + } + + /** + * @return The network associated with the record. In particular, this is + * the largest network where all the fields besides IP address have the + * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty + @JsonSerialize(using = ToStringSerializer.class) + public Network getNetwork() { + return network(); + } + /** * @return The IP risk of a model. + * @deprecated Use {@link #ipRisk()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_risk") public double getIpRisk() { - return this.ipRisk; + return ipRisk(); } } diff --git a/src/main/java/com/maxmind/geoip2/model/IspResponse.java b/src/main/java/com/maxmind/geoip2/model/IspResponse.java index 3bc9f2d51..83e21d014 100644 --- a/src/main/java/com/maxmind/geoip2/model/IspResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IspResponse.java @@ -3,65 +3,78 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; /** * This class provides the GeoIP2 ISP model. + * + * @param autonomousSystemNumber The autonomous system number associated with the IP address. + * @param autonomousSystemOrganization The organization associated with the registered autonomous + * system number for the IP address. + * @param ipAddress The IP address that the data in the model is for. + * @param isp The name of the ISP associated with the IP address. + * @param mobileCountryCode The + * mobile country code (MCC) associated with the IP address and ISP. + * This property is available from the City and Insights web services and + * the GeoIP2 Enterprise database. + * @param mobileNetworkCode The + * mobile network code (MNC) associated with the IP address and ISP. + * This property is available from the City and Insights web services and + * the GeoIP2 Enterprise database. + * @param organization The name of the organization associated with the IP address. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. */ -public class IspResponse extends AsnResponse { +public record IspResponse( + @JsonProperty("autonomous_system_number") + @MaxMindDbParameter(name = "autonomous_system_number") + Long autonomousSystemNumber, - private final String isp; - private final String organization; - private final String mobileCountryCode; - private final String mobileNetworkCode; + @JsonProperty("autonomous_system_organization") + @MaxMindDbParameter(name = "autonomous_system_organization") + String autonomousSystemOrganization, + + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, + + @JsonProperty("isp") + @MaxMindDbParameter(name = "isp") + String isp, + + @JsonProperty("mobile_country_code") + @MaxMindDbParameter(name = "mobile_country_code") + String mobileCountryCode, + + @JsonProperty("mobile_network_code") + @MaxMindDbParameter(name = "mobile_network_code") + String mobileNetworkCode, + + @JsonProperty("organization") + @MaxMindDbParameter(name = "organization") + String organization, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @MaxMindDbParameter(name = "network") + Network network +) implements JsonSerializable { /** - * Constructs an instance of {@code IspResponse}. - * - * @param autonomousSystemNumber the autonomous system number associated with the IP - * address - * @param autonomousSystemOrganization the organization associated with the registered - * autonomous system number for the IP address - * @param ipAddress the IP address that the data in the model is for - * @param isp the name of the ISP associated with the IP address - * @param mobileCountryCode the mobile country code (MCC) associated with the IP - * @param mobileNetworkCode the mobile network code (MNC) associated with the IP - * @param organization the name of the organization associated with the IP - * address - * @param network the network associated with the record + * Canonical constructor. */ @MaxMindDbConstructor - public IspResponse( - @JsonProperty("autonomous_system_number") - @MaxMindDbParameter(name = "autonomous_system_number") Long autonomousSystemNumber, - @JsonProperty("autonomous_system_organization") - @MaxMindDbParameter(name = "autonomous_system_organization") - String autonomousSystemOrganization, - @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @JsonProperty("isp") @MaxMindDbParameter(name = "isp") String isp, - @JsonProperty("mobile_country_code") @MaxMindDbParameter(name = "mobile_country_code") - String mobileCountryCode, - @JsonProperty("mobile_network_code") @MaxMindDbParameter(name = "mobile_network_code") - String mobileNetworkCode, - @JsonProperty("organization") @MaxMindDbParameter(name = "organization") - String organization, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) @MaxMindDbParameter(name = "network") - Network network - ) { - super(autonomousSystemNumber, autonomousSystemOrganization, ipAddress, network); - this.isp = isp; - this.mobileCountryCode = mobileCountryCode; - this.mobileNetworkCode = mobileNetworkCode; - this.organization = organization; + public IspResponse { } /** - * Constructs an instance of {@code IspResponse}. + * Constructs an instance of {@code IspResponse}. * * @param response The {@code AsnResponse} object to copy. * @param ipAddress The IP address that the data in the model is for. @@ -73,22 +86,57 @@ public IspResponse( Network network ) { this( - response.getAutonomousSystemNumber(), - response.getAutonomousSystemOrganization(), + response.autonomousSystemNumber(), + response.autonomousSystemOrganization(), ipAddress, - response.getIsp(), - response.getMobileCountryCode(), - response.getMobileNetworkCode(), - response.getOrganization(), + response.isp(), + response.mobileCountryCode(), + response.mobileNetworkCode(), + response.organization(), network ); } + /** + * @return The autonomous system number associated with the IP address. + * @deprecated Use {@link #autonomousSystemNumber()} instead. This method will be removed + * in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("autonomous_system_number") + public Long getAutonomousSystemNumber() { + return autonomousSystemNumber(); + } + + /** + * @return The organization associated with the registered autonomous system + * number for the IP address + * @deprecated Use {@link #autonomousSystemOrganization()} instead. This method will be + * removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("autonomous_system_organization") + public String getAutonomousSystemOrganization() { + return autonomousSystemOrganization(); + } + + /** + * @return The IP address that the data in the model is for. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("ip_address") + public String getIpAddress() { + return ipAddress(); + } + /** * @return The name of the ISP associated with the IP address. + * @deprecated Use {@link #isp()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getIsp() { - return this.isp; + return isp(); } /** @@ -96,10 +144,12 @@ public String getIsp() { * mobile country code (MCC) associated with the IP address and ISP. * This property is available from the City and Insights web services and * the GeoIP2 Enterprise database. + * @deprecated Use {@link #mobileCountryCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("mobile_country_code") public String getMobileCountryCode() { - return this.mobileCountryCode; + return mobileCountryCode(); } /** @@ -107,16 +157,33 @@ public String getMobileCountryCode() { * mobile network code (MNC) associated with the IP address and ISP. * This property is available from the City and Insights web services and * the GeoIP2 Enterprise database. + * @deprecated Use {@link #mobileNetworkCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("mobile_network_code") public String getMobileNetworkCode() { - return this.mobileNetworkCode; + return mobileNetworkCode(); } /** * @return The name of the organization associated with the IP address. + * @deprecated Use {@link #organization()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getOrganization() { - return this.organization; + return organization(); + } + + /** + * @return The network associated with the record. In particular, this is + * the largest network where all the fields besides IP address have the + * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty + @JsonSerialize(using = ToStringSerializer.class) + public Network getNetwork() { + return network(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/AbstractNamedRecord.java b/src/main/java/com/maxmind/geoip2/record/AbstractNamedRecord.java deleted file mode 100644 index c3f644aeb..000000000 --- a/src/main/java/com/maxmind/geoip2/record/AbstractNamedRecord.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.maxmind.geoip2.record; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Abstract class for records with name maps. - */ -public abstract class AbstractNamedRecord extends AbstractRecord { - - private final Map names; - private final Long geoNameId; - private final List locales; - - AbstractNamedRecord() { - this(null, null, null); - } - - AbstractNamedRecord(List locales, Long geoNameId, Map names) { - this.names = names != null ? names : new HashMap<>(); - this.geoNameId = geoNameId; - this.locales = locales != null ? locales : new ArrayList<>(); - } - - /** - * @return The GeoName ID for the city. - */ - @JsonProperty("geoname_id") - public Long getGeoNameId() { - return this.geoNameId; - } - - /** - * @return The name of the city based on the locales list passed to the - * constructor. - */ - @JsonIgnore - public String getName() { - for (String lang : this.locales) { - if (this.names.containsKey(lang)) { - return this.names.get(lang); - } - } - return null; - } - - /** - * @return A {@link Map} from locale codes to the name in that locale. - */ - @JsonProperty("names") - public Map getNames() { - return new HashMap<>(this.names); - } -} diff --git a/src/main/java/com/maxmind/geoip2/record/City.java b/src/main/java/com/maxmind/geoip2/record/City.java index 98b5da9ef..223870e9b 100644 --- a/src/main/java/com/maxmind/geoip2/record/City.java +++ b/src/main/java/com/maxmind/geoip2/record/City.java @@ -4,6 +4,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.NamedRecord; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -13,38 +16,48 @@ *

*

* Do not use any of the city names as a database or map key. Use the value - * returned by {@link #getGeoNameId} instead. + * returned by {@link #geonameId()} instead. *

+ * + * @param locales The locales to use for retrieving localized names. + * @param confidence A value from 0-100 indicating MaxMind's confidence that the city + * is correct. This attribute is only available from the Insights + * web service and the GeoIP2 Enterprise database. + * @param geonameId The GeoName ID for the city. + * @param names A {@link Map} from locale codes to the name in that locale. */ -public final class City extends AbstractNamedRecord { +public record City( + @JacksonInject("locales") + @MaxMindDbParameter(name = "locales") + List locales, - private final Integer confidence; + @JsonProperty("confidence") + @MaxMindDbParameter(name = "confidence") + Integer confidence, + + @JsonProperty("geoname_id") + @MaxMindDbParameter(name = "geoname_id") + Long geonameId, + + @JsonProperty("names") + @MaxMindDbParameter(name = "names") + Map names +) implements NamedRecord { /** - * Constructs an instance of {@code City} with no data. + * Compact canonical constructor that ensures immutability and handles null values. */ - public City() { - this(null, null, null, null); + @MaxMindDbConstructor + public City { + locales = locales != null ? List.copyOf(locales) : List.of(); + names = names != null ? Map.copyOf(names) : Map.of(); } /** - * Constructs an instance of {@code City} with the specified parameters. - * - * @param locales The locales to use. - * @param confidence A value from 0-100 indicating MaxMind's confidence that the - * city is correct. . - * @param geoNameId The GeoName ID for the city. - * @param names A map from locale codes to the city name in that locale. + * Constructs an instance of {@code City} with no data. */ - @MaxMindDbConstructor - public City( - @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, - @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geoNameId, - @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names - ) { - super(locales, geoNameId, names); - this.confidence = confidence; + public City() { + this(null, null, null, null); } /** @@ -59,9 +72,9 @@ public City( ) { this( locales, - city.getConfidence(), - city.getGeoNameId(), - city.getNames() + city.confidence(), + city.geonameId(), + city.names() ); } @@ -69,8 +82,41 @@ public City( * @return A value from 0-100 indicating MaxMind's confidence that the city * is correct. This attribute is only available from the Insights * web service and the GeoIP2 Enterprise database. + * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public Integer getConfidence() { - return this.confidence; + return confidence(); + } + + /** + * @return The GeoName ID for the city. + * @deprecated Use {@link #geonameId()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("geoname_id") + public Long getGeoNameId() { + return geonameId(); + } + + /** + * @return The name of the city based on the locales list passed to the + * constructor. + * @deprecated Use {@link #name()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @com.fasterxml.jackson.annotation.JsonIgnore + public String getName() { + return name(); + } + + /** + * @return A {@link Map} from locale codes to the name in that locale. + * @deprecated Use {@link #names()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("names") + public Map getNames() { + return names(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/Continent.java b/src/main/java/com/maxmind/geoip2/record/Continent.java index a103f3dc4..435ab586a 100644 --- a/src/main/java/com/maxmind/geoip2/record/Continent.java +++ b/src/main/java/com/maxmind/geoip2/record/Continent.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.NamedRecord; import java.util.List; import java.util.Map; @@ -13,38 +14,47 @@ *

*

* Do not use any of the continent names as a database or map key. Use the - * value returned by {@link #getGeoNameId} or {@link #getCode} instead. + * value returned by {@link #geonameId()} or {@link #code()} instead. *

+ * + * @param locales The locales to use for retrieving localized names. + * @param code A two character continent code like "NA" (North America) or "OC" + * (Oceania). + * @param geonameId The GeoName ID for the continent. + * @param names A {@link Map} from locale codes to the name in that locale. */ -public final class Continent extends AbstractNamedRecord { +public record Continent( + @JacksonInject("locales") + @MaxMindDbParameter(name = "locales") + List locales, - private final String code; + @JsonProperty("code") + @MaxMindDbParameter(name = "code") + String code, + + @JsonProperty("geoname_id") + @MaxMindDbParameter(name = "geoname_id") + Long geonameId, + + @JsonProperty("names") + @MaxMindDbParameter(name = "names") + Map names +) implements NamedRecord { /** - * Constructs an instance of {@code Continent} with no data. + * Compact canonical constructor that ensures immutability and handles null values. */ - public Continent() { - this(null, null, null, null); + @MaxMindDbConstructor + public Continent { + locales = locales != null ? List.copyOf(locales) : List.of(); + names = names != null ? Map.copyOf(names) : Map.of(); } /** - * Constructs an instance of {@code Continent}. - * - * @param locales The locales to use. - * @param code A two character continent code like "NA" (North America) or - * "OC" (Oceania). - * @param geoNameId The GeoName ID for the continent. - * @param names A map from locale codes to the continent names. + * Constructs an instance of {@code Continent} with no data. */ - @MaxMindDbConstructor - public Continent( - @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, - @JsonProperty("code") @MaxMindDbParameter(name = "code") String code, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geoNameId, - @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names - ) { - super(locales, geoNameId, names); - this.code = code; + public Continent() { + this(null, null, null, null); } /** @@ -59,18 +69,49 @@ public Continent( ) { this( locales, - continent.getCode(), - continent.getGeoNameId(), - continent.getNames() + continent.code(), + continent.geonameId(), + continent.names() ); } /** * @return A two character continent code like "NA" (North America) or "OC" * (Oceania). + * @deprecated Use {@link #code()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getCode() { - return this.code; + return code(); + } + + /** + * @return The GeoName ID for the continent. + * @deprecated Use {@link #geonameId()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("geoname_id") + public Long getGeoNameId() { + return geonameId(); + } + + /** + * @return The name of the continent based on the locales list. + * @deprecated Use {@link #name()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @com.fasterxml.jackson.annotation.JsonIgnore + public String getName() { + return name(); } + /** + * @return A {@link Map} from locale codes to the name in that locale. + * @deprecated Use {@link #names()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("names") + public Map getNames() { + return names(); + } } diff --git a/src/main/java/com/maxmind/geoip2/record/Country.java b/src/main/java/com/maxmind/geoip2/record/Country.java index 1db6309d5..9425d91c3 100644 --- a/src/main/java/com/maxmind/geoip2/record/Country.java +++ b/src/main/java/com/maxmind/geoip2/record/Country.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.NamedRecord; import java.util.List; import java.util.Map; @@ -13,29 +14,61 @@ *

*

* Do not use any of the country names as a database or map key. Use the value - * returned by {@link #getGeoNameId} or {@link #getIsoCode} instead. + * returned by {@link #geonameId()} or {@link #isoCode()} instead. *

+ * + * @param locales The locales to use for retrieving localized names. + * @param confidence A value from 0-100 indicating MaxMind's confidence that the + * country is correct. This attribute is only available from the + * Insights web service and the GeoIP2 Enterprise database. + * @param geonameId The GeoName ID for the country. + * @param isInEuropeanUnion This is true if the country is a member state of the + * European Union. + * @param isoCode The two-character ISO + * 3166-1 alpha code for the country. + * @param names A {@link Map} from locale codes to the name in that locale. */ -public class Country extends AbstractNamedRecord { +public record Country( + @JacksonInject("locales") + @MaxMindDbParameter(name = "locales") + List locales, - private final Integer confidence; - private final boolean isInEuropeanUnion; - private final String isoCode; + @JsonProperty("confidence") + @MaxMindDbParameter(name = "confidence") + Integer confidence, + + @JsonProperty("geoname_id") + @MaxMindDbParameter(name = "geoname_id") + Long geonameId, + + @JsonProperty("is_in_european_union") + @MaxMindDbParameter(name = "is_in_european_union") + boolean isInEuropeanUnion, + + @JsonProperty("iso_code") + @MaxMindDbParameter(name = "iso_code") + String isoCode, + + @JsonProperty("names") + @MaxMindDbParameter(name = "names") + Map names +) implements NamedRecord { /** - * Constructs an instance of {@code Country} with no data. + * Compact canonical constructor that ensures immutability and handles null values. */ - public Country() { - this(null, null, null, false, null, null); + public Country { + locales = locales != null ? List.copyOf(locales) : List.of(); + names = names != null ? Map.copyOf(names) : Map.of(); } - /** + /** * Constructs an instance of {@code Country}. * * @param locales The locales to use. * @param confidence This is a value from 0-100 indicating MaxMind's * confidence that the country is correct. - * @param geoNameId This is a GeoName ID for the country. + * @param geonameId This is a GeoName ID for the country. * @param isInEuropeanUnion This is true if the country is a member state of * the European Union. * @param isoCode This is a string up to three characters long contain the @@ -47,16 +80,27 @@ public Country() { public Country( @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geoNameId, + @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geonameId, @JsonProperty("is_in_european_union") @MaxMindDbParameter(name = "is_in_european_union") Boolean isInEuropeanUnion, @JsonProperty("iso_code") @MaxMindDbParameter(name = "iso_code") String isoCode, @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names ) { - super(locales, geoNameId, names); - this.confidence = confidence; - this.isInEuropeanUnion = isInEuropeanUnion != null ? isInEuropeanUnion : false; - this.isoCode = isoCode; + this( + locales, + confidence, + geonameId, + isInEuropeanUnion != null ? isInEuropeanUnion : false, + isoCode, + names + ); + } + + /** + * Constructs an instance of {@code Country} with no data. + */ + public Country() { + this(null, null, null, false, null, null); } /** @@ -71,11 +115,11 @@ public Country( ) { this( locales, - country.getConfidence(), - country.getGeoNameId(), + country.confidence(), + country.geonameId(), country.isInEuropeanUnion(), - country.getIsoCode(), - country.getNames() + country.isoCode(), + country.names() ); } @@ -83,27 +127,52 @@ public Country( * @return A value from 0-100 indicating MaxMind's confidence that the * country is correct. This attribute is only available from the * Insights web service and the GeoIP2 Enterprise database. + * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public Integer getConfidence() { - return this.confidence; - } - - /** - * @return This is true if the country is a member state of the European - * Union. - */ - @JsonProperty("is_in_european_union") - public boolean isInEuropeanUnion() { - return this.isInEuropeanUnion; + return confidence(); } /** * @return The two-character ISO * 3166-1 alpha code for the country. + * @deprecated Use {@link #isoCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("iso_code") public String getIsoCode() { - return this.isoCode; + return isoCode(); + } + + /** + * @return The GeoName ID for the country. + * @deprecated Use {@link #geonameId()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("geoname_id") + public Long getGeoNameId() { + return geonameId(); + } + + /** + * @return The name of the country based on the locales list. + * @deprecated Use {@link #name()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @com.fasterxml.jackson.annotation.JsonIgnore + public String getName() { + return name(); + } + + /** + * @return A {@link Map} from locale codes to the name in that locale. + * @deprecated Use {@link #names()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("names") + public Map getNames() { + return names(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/Location.java b/src/main/java/com/maxmind/geoip2/record/Location.java index a1be28a28..6a1fbf918 100644 --- a/src/main/java/com/maxmind/geoip2/record/Location.java +++ b/src/main/java/com/maxmind/geoip2/record/Location.java @@ -3,100 +3,108 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.JsonSerializable; /** *

* Contains data for the location record associated with an IP address. *

+ * + * @param accuracyRadius The approximate accuracy radius in kilometers around the + * latitude and longitude for the IP address. This is the radius + * where we have a 67% confidence that the device using the IP + * address resides within the circle centered at the latitude and + * longitude with the provided radius. + * @param averageIncome The average income in US dollars associated with the requested + * IP address. This attribute is only available from the Insights + * web service. + * @param latitude The approximate latitude of the location associated with the IP + * address. This value is not precise and should not be used to identify + * a particular address or household. + * @param longitude The approximate longitude of the location associated with the IP + * address. This value is not precise and should not be used to identify + * a particular address or household. + * @param populationDensity The estimated population per square kilometer associated with + * the IP address. This attribute is only available from the + * Insights web service. + * @param timeZone The time zone associated with location, as specified by the + * IANA Time Zone Database, + * e.g., "America/New_York". */ -public class Location extends AbstractRecord { +public record Location( + @JsonProperty("accuracy_radius") + @MaxMindDbParameter(name = "accuracy_radius") + Integer accuracyRadius, + + @JsonProperty("average_income") + @MaxMindDbParameter(name = "average_income") + Integer averageIncome, - private final Integer accuracyRadius; - private final Integer averageIncome; - private final Double latitude; - private final Double longitude; - private final Integer populationDensity; - private final String timeZone; + @JsonProperty("latitude") + @MaxMindDbParameter(name = "latitude") + Double latitude, + + @JsonProperty("longitude") + @MaxMindDbParameter(name = "longitude") + Double longitude, + + @JsonProperty("population_density") + @MaxMindDbParameter(name = "population_density") + Integer populationDensity, + + @JsonProperty("time_zone") + @MaxMindDbParameter(name = "time_zone") + String timeZone +) implements JsonSerializable { /** - * Constructs a {@code Location} record with {@code null} values for all the fields. + * Compact canonical constructor. */ - public Location() { - this(null, null, null, null, null, null); + @MaxMindDbConstructor + public Location { } /** - * Constructs an instance of {@code Location}. - * - * @param accuracyRadius The approximate accuracy radius in kilometers - * around the latitude and longitude for the IP address. This is the radius - * where we have a 67% confidence that the device using the IP address - * resides within the circle centered at the latitude and longitude with - * the provided radius. - * @param averageIncome The average income in US dollars associated with - * the requested IP address. This attribute is only available from the - * Insights web service. - * @param latitude The approximate latitude of the location associated - * with the IP address. This value is not precise and should not be used - * to identify a particular address or household. - * @param longitude The approximate longitude of the location associated - * with the IP address. This value is not precise and should not be used - * to identify a particular address or household. - * @param populationDensity The estimated population per square kilometer - * associated with the IP address. This attribute is only available from - * the Insights web service. - * @param timeZone The time zone associated with location, as specified by - * the IANA Time Zone - * Database, e.g., "America/New_York". + * Constructs a {@code Location} record with {@code null} values for all the fields. */ - @MaxMindDbConstructor - public Location( - @JsonProperty("accuracy_radius") @MaxMindDbParameter(name = "accuracy_radius") - Integer accuracyRadius, - @JsonProperty("average_income") @MaxMindDbParameter(name = "average_income") - Integer averageIncome, - @JsonProperty("latitude") @MaxMindDbParameter(name = "latitude") Double latitude, - @JsonProperty("longitude") @MaxMindDbParameter(name = "longitude") Double longitude, - @JsonProperty("population_density") @MaxMindDbParameter(name = "population_density") - Integer populationDensity, - @JsonProperty("time_zone") @MaxMindDbParameter(name = "time_zone") String timeZone - ) { - this.accuracyRadius = accuracyRadius; - this.averageIncome = averageIncome; - this.latitude = latitude; - this.longitude = longitude; - this.populationDensity = populationDensity; - this.timeZone = timeZone; + public Location() { + this(null, null, null, null, null, null); } /** * @return The average income in US dollars associated with the requested * IP address. This attribute is only available from the Insights web * service. + * @deprecated Use {@link #averageIncome()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("average_income") public Integer getAverageIncome() { - return this.averageIncome; + return averageIncome(); } /** * @return The estimated population per square kilometer associated with the * IP address. This attribute is only available from the Insights web * service. + * @deprecated Use {@link #populationDensity()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("population_density") public Integer getPopulationDensity() { - return this.populationDensity; + return populationDensity(); } /** * @return The time zone associated with location, as specified by the IANA Time Zone * Database, e.g., "America/New_York". + * @deprecated Use {@link #timeZone()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("time_zone") public String getTimeZone() { - return this.timeZone; + return timeZone(); } /** @@ -105,28 +113,33 @@ public String getTimeZone() { * have a 67% confidence that the device using the IP address resides * within the circle centered at the latitude and longitude with the * provided radius. + * @deprecated Use {@link #accuracyRadius()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("accuracy_radius") public Integer getAccuracyRadius() { - return this.accuracyRadius; + return accuracyRadius(); } - /** * @return The approximate latitude of the location associated with the * IP address. This value is not precise and should not be used to * identify a particular address or household. + * @deprecated Use {@link #latitude()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public Double getLatitude() { - return this.latitude; + return latitude(); } /** * @return The approximate longitude of the location associated with the * IP address. This value is not precise and should not be used to * identify a particular address or household. + * @deprecated Use {@link #longitude()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public Double getLongitude() { - return this.longitude; + return longitude(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/MaxMind.java b/src/main/java/com/maxmind/geoip2/record/MaxMind.java index 6bdd9ef68..8d6caea3a 100644 --- a/src/main/java/com/maxmind/geoip2/record/MaxMind.java +++ b/src/main/java/com/maxmind/geoip2/record/MaxMind.java @@ -3,42 +3,44 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.JsonSerializable; /** *

* Contains data related to your MaxMind account. *

+ * + * @param queriesRemaining The number of remaining queries in your account for the current + * web service. This returns {@code null} when called on a database. */ -public final class MaxMind extends AbstractRecord { - - private final Integer queriesRemaining; +public record MaxMind( + @JsonProperty("queries_remaining") + @MaxMindDbParameter(name = "queries_remaining") + Integer queriesRemaining +) implements JsonSerializable { /** - * Constructs a {@code MaxMind} record. + * Compact canonical constructor. */ - public MaxMind() { - this(null); + @MaxMindDbConstructor + public MaxMind { } /** - * Constructs a {@code MaxMind} record. - * - * @param queriesRemaining The number of remaining queries in the current web service call. - * This returns {@code null} when called on a database. + * Constructs a {@code MaxMind} record. */ - @MaxMindDbConstructor - public MaxMind( - @JsonProperty("queries_remaining") @MaxMindDbParameter(name = "queries_remaining") - Integer queriesRemaining) { - this.queriesRemaining = queriesRemaining; + public MaxMind() { + this(null); } /** * @return The number of remaining queried in your account for the current * web service. This returns {@code null} when called on a database. + * @deprecated Use {@link #queriesRemaining()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("queries_remaining") public Integer getQueriesRemaining() { - return this.queriesRemaining; + return queriesRemaining(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/Postal.java b/src/main/java/com/maxmind/geoip2/record/Postal.java index 05435116a..21393164c 100644 --- a/src/main/java/com/maxmind/geoip2/record/Postal.java +++ b/src/main/java/com/maxmind/geoip2/record/Postal.java @@ -3,58 +3,63 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.JsonSerializable; /** *

* Contains data for the postal record associated with an IP address. *

+ * + * @param code The postal code of the location. Postal codes are not available for all + * countries. In some countries, this will only contain part of the postal + * code. + * @param confidence A value from 0-100 indicating MaxMind's confidence that the postal + * code is correct. This attribute is only available from the Insights + * web service and the GeoIP2 Enterprise database. */ -public final class Postal extends AbstractRecord { +public record Postal( + @JsonProperty("code") + @MaxMindDbParameter(name = "code") + String code, - private final String code; - private final Integer confidence; + @JsonProperty("confidence") + @MaxMindDbParameter(name = "confidence") + Integer confidence +) implements JsonSerializable { /** - * Constructs a {@code Postal} record. + * Compact canonical constructor. */ - public Postal() { - this(null, null); + @MaxMindDbConstructor + public Postal { } /** - * Constructs an instance of {@code Postal}. - * - * @param code The postal code of the location. Postal codes are not available - * for all countries. In some countries, this will only contain part - * of the postal code. - * @param confidence A value from 0-100 indicating MaxMind's confidence that the - * postal code is correct. This attribute is only available from the - * Insights web service and the GeoIP2 Enterprise database. + * Constructs a {@code Postal} record. */ - @MaxMindDbConstructor - public Postal( - @JsonProperty("code") @MaxMindDbParameter(name = "code") String code, - @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence - ) { - this.code = code; - this.confidence = confidence; + public Postal() { + this(null, null); } /** * @return The postal code of the location. Postal codes are not available * for all countries. In some countries, this will only contain part * of the postal code. + * @deprecated Use {@link #code()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getCode() { - return this.code; + return code(); } /** * @return A value from 0-100 indicating MaxMind's confidence that the * postal code is correct. This attribute is only available from the * Insights web service and the GeoIP2 Enterprise database. + * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public Integer getConfidence() { - return this.confidence; + return confidence(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java b/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java index 61c1a1876..40e779d84 100644 --- a/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java +++ b/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.NamedRecord; import java.util.List; import java.util.Map; @@ -18,18 +19,59 @@ *

*

* Do not use any of the country names as a database or map key. Use the value - * returned by {@link #getGeoNameId} or {@link #getIsoCode} instead. + * returned by {@link #geonameId()} or {@link #isoCode()} instead. *

+ * + * @param locales The locales to use for retrieving localized names. + * @param confidence A value from 0-100 indicating MaxMind's confidence that the + * country is correct. This attribute is only available from the + * Insights web service and the GeoIP2 Enterprise database. + * @param geonameId The GeoName ID for the country. + * @param isInEuropeanUnion This is true if the country is a member state of the + * European Union. + * @param isoCode The two-character ISO + * 3166-1 alpha code for the country. + * @param names A {@link Map} from locale codes to the name in that locale. + * @param type A string indicating the type of entity that is representing the + * country. Currently, we only return {@code military} but this could + * expand to include other types in the future. */ -public final class RepresentedCountry extends Country { +public record RepresentedCountry( + @JacksonInject("locales") + @MaxMindDbParameter(name = "locales") + List locales, - private final String type; + @JsonProperty("confidence") + @MaxMindDbParameter(name = "confidence") + Integer confidence, + + @JsonProperty("geoname_id") + @MaxMindDbParameter(name = "geoname_id") + Long geonameId, + + @JsonProperty("is_in_european_union") + @MaxMindDbParameter(name = "is_in_european_union") + boolean isInEuropeanUnion, + + @JsonProperty("iso_code") + @MaxMindDbParameter(name = "iso_code") + String isoCode, + + @JsonProperty("names") + @MaxMindDbParameter(name = "names") + Map names, + + @JsonProperty("type") + @MaxMindDbParameter(name = "type") + String type +) implements NamedRecord { /** - * Constructs an instance of {@code RepresentedCountry} with no data. + * Compact canonical constructor that ensures immutability and handles null values. */ - public RepresentedCountry() { - this(null, null, null, false, null, null, null); + public RepresentedCountry { + locales = locales != null ? List.copyOf(locales) : List.of(); + names = names != null ? Map.copyOf(names) : Map.of(); } /** @@ -38,7 +80,7 @@ public RepresentedCountry() { * @param locales The locales to use. * @param confidence This is a value from 0-100 indicating MaxMind's * confidence that the country is correct. - * @param geoNameId This is a GeoName ID for the country. + * @param geonameId This is a GeoName ID for the country. * @param isInEuropeanUnion This is true if the country is a member state of * the European Union. * @param isoCode This is a string up to three characters long contain the @@ -52,19 +94,32 @@ public RepresentedCountry() { public RepresentedCountry( @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geoNameId, + @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geonameId, @JsonProperty("is_in_european_union") @MaxMindDbParameter(name = "is_in_european_union") Boolean isInEuropeanUnion, @JsonProperty("iso_code") @MaxMindDbParameter(name = "iso_code") String isoCode, @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names, @JsonProperty("type") @MaxMindDbParameter(name = "type") String type ) { - super(locales, confidence, geoNameId, isInEuropeanUnion, isoCode, - names); - this.type = type; + this( + locales, + confidence, + geonameId, + isInEuropeanUnion != null ? isInEuropeanUnion : false, + isoCode, + names, + type + ); } - /** + /** + * Constructs an instance of {@code RepresentedCountry} with no data. + */ + public RepresentedCountry() { + this(null, null, null, false, null, null, null); + } + + /** * Constructs an instance of {@code RepresentedCountry}. * * @param country The {@code RepresentedCountry} object to copy. @@ -76,12 +131,12 @@ public RepresentedCountry( ) { this( locales, - country.getConfidence(), - country.getGeoNameId(), + country.confidence(), + country.geonameId(), country.isInEuropeanUnion(), - country.getIsoCode(), - country.getNames(), - country.getType() + country.isoCode(), + country.names(), + country.type() ); } @@ -89,9 +144,63 @@ public RepresentedCountry( * @return A string indicating the type of entity that is representing the * country. Currently, we only return {@code military} but this could * expand to include other types in the future. + * @deprecated Use {@link #type()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getType() { - return this.type; + return type(); } + /** + * @return A value from 0-100 indicating MaxMind's confidence that the + * country is correct. This attribute is only available from the + * Insights web service and the GeoIP2 Enterprise database. + * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Integer getConfidence() { + return confidence(); + } + + /** + * @return The two-character ISO + * 3166-1 alpha code for the country. + * @deprecated Use {@link #isoCode()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("iso_code") + public String getIsoCode() { + return isoCode(); + } + + /** + * @return The GeoName ID for the country. + * @deprecated Use {@link #geonameId()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("geoname_id") + public Long getGeoNameId() { + return geonameId(); + } + + /** + * @return The name of the country based on the locales list. + * @deprecated Use {@link #name()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @com.fasterxml.jackson.annotation.JsonIgnore + public String getName() { + return name(); + } + + /** + * @return A {@link Map} from locale codes to the name in that locale. + * @deprecated Use {@link #names()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("names") + public Map getNames() { + return names(); + } } diff --git a/src/main/java/com/maxmind/geoip2/record/Subdivision.java b/src/main/java/com/maxmind/geoip2/record/Subdivision.java index 3a1cd4af4..80c11f537 100644 --- a/src/main/java/com/maxmind/geoip2/record/Subdivision.java +++ b/src/main/java/com/maxmind/geoip2/record/Subdivision.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; +import com.maxmind.geoip2.NamedRecord; import java.util.List; import java.util.Map; @@ -13,13 +14,49 @@ *

*

* Do not use any of the subdivision names as a database or map key. Use the - * value returned by {@link #getGeoNameId} or {@link #getIsoCode} instead. + * value returned by {@link #geonameId()} or {@link #isoCode()} instead. *

+ * + * @param locales The locales to use for retrieving localized names. + * @param confidence A value from 0-100 indicating MaxMind's confidence that the + * subdivision is correct. This attribute is only available from + * the Insights web service and the GeoIP2 Enterprise database. + * @param geonameId The GeoName ID for the subdivision. + * @param isoCode A string up to three characters long containing the subdivision + * portion of the ISO + * 3166-2 code. + * @param names A {@link Map} from locale codes to the name in that locale. */ -public final class Subdivision extends AbstractNamedRecord { +public record Subdivision( + @JacksonInject("locales") + @MaxMindDbParameter(name = "locales") + List locales, - private final Integer confidence; - private final String isoCode; + @JsonProperty("confidence") + @MaxMindDbParameter(name = "confidence") + Integer confidence, + + @JsonProperty("geoname_id") + @MaxMindDbParameter(name = "geoname_id") + Long geonameId, + + @JsonProperty("iso_code") + @MaxMindDbParameter(name = "iso_code") + String isoCode, + + @JsonProperty("names") + @MaxMindDbParameter(name = "names") + Map names +) implements NamedRecord { + + /** + * Compact canonical constructor that ensures immutability and handles null values. + */ + @MaxMindDbConstructor + public Subdivision { + locales = locales != null ? List.copyOf(locales) : List.of(); + names = names != null ? Map.copyOf(names) : Map.of(); + } /** * Constructs a {@code Subdivision} record. @@ -28,29 +65,6 @@ public Subdivision() { this(null, null, null, null, null); } - /** - * Constructs an instance of {@code Subdivision}. - * - * @param locales The locales to use. - * @param confidence This is a value from 0-100 indicating MaxMind's - * confidence that the subdivision is correct. - * @param geoNameId This is a GeoName ID for the subdivision. - * @param isoCode This is a string up to three characters long contain the subdivision code. - * @param names This is a map from locale codes to the names for the subdivision in that locale. - */ - @MaxMindDbConstructor - public Subdivision( - @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, - @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geoNameId, - @JsonProperty("iso_code") @MaxMindDbParameter(name = "iso_code") String isoCode, - @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names - ) { - super(locales, geoNameId, names); - this.confidence = confidence; - this.isoCode = isoCode; - } - /** * Constructs an instance of {@code Subdivision} with the specified parameters. * @@ -63,10 +77,10 @@ public Subdivision( ) { this( locales, - subdivision.getConfidence(), - subdivision.getGeoNameId(), - subdivision.getIsoCode(), - subdivision.getNames() + subdivision.confidence(), + subdivision.geonameId(), + subdivision.isoCode(), + subdivision.names() ); } @@ -74,10 +88,12 @@ public Subdivision( * @return This is a value from 0-100 indicating MaxMind's confidence that * the subdivision is correct. This attribute is only available from * the Insights web service and the GeoIP2 Enterprise database. + * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("confidence") public Integer getConfidence() { - return this.confidence; + return confidence(); } /** @@ -85,9 +101,41 @@ public Integer getConfidence() { * subdivision portion of the ISO * 3166-2code. + * @deprecated Use {@link #isoCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("iso_code") public String getIsoCode() { - return this.isoCode; + return isoCode(); + } + + /** + * @return The GeoName ID for the subdivision. + * @deprecated Use {@link #geonameId()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("geoname_id") + public Long getGeoNameId() { + return geonameId(); + } + + /** + * @return The name of the subdivision based on the locales list. + * @deprecated Use {@link #name()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @com.fasterxml.jackson.annotation.JsonIgnore + public String getName() { + return name(); + } + + /** + * @return A {@link Map} from locale codes to the name in that locale. + * @deprecated Use {@link #names()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + @JsonProperty("names") + public Map getNames() { + return names(); } } diff --git a/src/main/java/com/maxmind/geoip2/record/Traits.java b/src/main/java/com/maxmind/geoip2/record/Traits.java index 0330fcdf6..6bfba9aab 100644 --- a/src/main/java/com/maxmind/geoip2/record/Traits.java +++ b/src/main/java/com/maxmind/geoip2/record/Traits.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.record; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -8,35 +7,163 @@ import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; +import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; import com.maxmind.geoip2.model.ConnectionTypeResponse.ConnectionType; /** * Contains data for the traits record associated with an IP address. + * + * @param autonomousSystemNumber The autonomous system number associated with the IP address. + * This is only available from the City Plus and Insights web + * services and the Enterprise database. + * @param autonomousSystemOrganization The organization associated with the registered autonomous system number for the IP address. This is + * only available from the City Plus and Insights web services + * and the Enterprise database. + * @param connectionType The connection type of the IP address. This is only available from the + * City Plus and Insights web services and the Enterprise database. + * @param domain The second level domain associated with the IP address. This will be something + * like "example.com" or "example.co.uk", not "foo.example.com". This is only + * available from the City Plus and Insights web services and the Enterprise + * database. + * @param ipAddress The IP address that the data in the model is for. If you performed a "me" + * lookup against the web service, this will be the externally routable IP + * address for the system the code is running on. If the system is behind a + * NAT, this may differ from the IP address locally assigned to it. + * @param isAnonymous This is true if the IP address belongs to any sort of anonymous network. + * @param isAnonymousVpn This is true if the IP address belongs to an anonymous VPN system. + * @param isAnycast This is true if the IP address is an anycast address. + * @param isHostingProvider This is true if the IP address belongs to a hosting provider. + * @param isLegitimateProxy This is true if the IP address belongs to a legitimate proxy. + * @param isPublicProxy This is true if the IP address belongs to a public proxy. + * @param isResidentialProxy This is true if the IP address is on a suspected anonymizing network + * and belongs to a residential ISP. + * @param isTorExitNode This is true if the IP address is a Tor exit node. + * @param isp The name of the ISP associated with the IP address. This is only available from + * the City Plus and Insights web services and the Enterprise database. + * @param mobileCountryCode The + * mobile country code (MCC) associated with the IP address and ISP. + * This is available from the City Plus and Insights web services and + * the Enterprise database. + * @param mobileNetworkCode The + * mobile network code (MNC) associated with the IP address and ISP. + * This is available from the City Plus and Insights web services and + * the Enterprise database. + * @param network The network associated with the record. In particular, this is the largest + * network where all the fields besides IP address have the same value. + * @param organization The name of the organization associated with the IP address. This is only + * available from the City Plus and Insights web services and the Enterprise + * database. + * @param userType The user type associated with the IP address. This can be one of the following + * values: business, cafe, cellular, college, consumer_privacy_network, + * content_delivery_network, dialup, government, hosting, library, military, + * residential, router, school, search_engine_spider, traveler. This is only + * available from the Insights web service and the Enterprise database. + * @param userCount The estimated number of users sharing the IP address/network during the past + * 24 hours. For IPv4, the count is for the individual IP address. For IPv6, the + * count is for the /64 network. This is only available from the Insights web + * service. + * @param staticIpScore The static IP score of the IP address. This is an indicator of how static + * or dynamic an IP address is. This is only available from the Insights web + * service. */ -public final class Traits extends AbstractRecord { - - private final Long autonomousSystemNumber; - private final String autonomousSystemOrganization; - private final ConnectionType connectionType; - private final String domain; - private final String ipAddress; - private final boolean isAnonymous; - private final boolean isAnonymousVpn; - private final boolean isAnycast; - private final boolean isHostingProvider; - private final boolean isLegitimateProxy; - private final boolean isPublicProxy; - private final boolean isResidentialProxy; - private final boolean isTorExitNode; - private final String isp; - private final String mobileCountryCode; - private final String mobileNetworkCode; - private final Network network; - private final String organization; - private final String userType; - private final Integer userCount; - private final Double staticIpScore; +public record Traits( + @JsonProperty("autonomous_system_number") + @MaxMindDbParameter(name = "autonomous_system_number") + Long autonomousSystemNumber, + + @JsonProperty("autonomous_system_organization") + @MaxMindDbParameter(name = "autonomous_system_organization") + String autonomousSystemOrganization, + + @JsonProperty("connection_type") + @MaxMindDbParameter(name = "connection_type") + ConnectionType connectionType, + + @JsonProperty("domain") + @MaxMindDbParameter(name = "domain") + String domain, + + @JsonProperty("ip_address") + @MaxMindDbParameter(name = "ip_address") + String ipAddress, + + @JsonProperty("is_anonymous") + @MaxMindDbParameter(name = "is_anonymous") + boolean isAnonymous, + + @JsonProperty("is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn") + boolean isAnonymousVpn, + + @JsonProperty("is_anycast") + @MaxMindDbParameter(name = "is_anycast") + boolean isAnycast, + + @JsonProperty("is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider") + boolean isHostingProvider, + + @JsonProperty("is_legitimate_proxy") + @MaxMindDbParameter(name = "is_legitimate_proxy") + boolean isLegitimateProxy, + + @JsonProperty("is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy") + boolean isPublicProxy, + + @JsonProperty("is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy") + boolean isResidentialProxy, + + @JsonProperty("is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node") + boolean isTorExitNode, + + @JsonProperty("isp") + @MaxMindDbParameter(name = "isp") + String isp, + + @JsonProperty("mobile_country_code") + @MaxMindDbParameter(name = "mobile_country_code") + String mobileCountryCode, + + @JsonProperty("mobile_network_code") + @MaxMindDbParameter(name = "mobile_network_code") + String mobileNetworkCode, + + @JsonProperty("network") + @JsonDeserialize(using = NetworkDeserializer.class) + @JsonSerialize(using = ToStringSerializer.class) + @MaxMindDbParameter(name = "network") + Network network, + + @JsonProperty("organization") + @MaxMindDbParameter(name = "organization") + String organization, + + @JsonProperty("user_type") + @MaxMindDbParameter(name = "user_type") + String userType, + + @JsonProperty("user_count") + @MaxMindDbParameter(name = "user_count") + Integer userCount, + + @JsonProperty("static_ip_score") + @MaxMindDbParameter(name = "static_ip_score") + Double staticIpScore +) implements JsonSerializable { + + /** + * Compact canonical constructor. + */ + public Traits { + } /** * Constructs an instance of {@code Traits}. @@ -86,78 +213,6 @@ public Traits(String ipAddress, Network network) { * @param userCount the user count * @param staticIpScore the static IP score */ - public Traits( - @JsonProperty("autonomous_system_number") Long autonomousSystemNumber, - @JsonProperty("autonomous_system_organization") String autonomousSystemOrganization, - @JsonProperty("connection_type") ConnectionType connectionType, - @JsonProperty("domain") String domain, - @JsonProperty("ip_address") String ipAddress, - @JsonProperty("is_anonymous") boolean isAnonymous, - @JsonProperty("is_anonymous_vpn") boolean isAnonymousVpn, - @JsonProperty("is_anycast") boolean isAnycast, - @JsonProperty("is_hosting_provider") boolean isHostingProvider, - @JsonProperty("is_legitimate_proxy") boolean isLegitimateProxy, - @JsonProperty("is_public_proxy") boolean isPublicProxy, - @JsonProperty("is_residential_proxy") boolean isResidentialProxy, - @JsonProperty("is_tor_exit_node") boolean isTorExitNode, - @JsonProperty("isp") String isp, - @JsonProperty("mobile_country_code") String mobileCountryCode, - @JsonProperty("mobile_network_code") String mobileNetworkCode, - @JsonProperty("network") - @JsonDeserialize(using = NetworkDeserializer.class) Network network, - @JsonProperty("organization") String organization, - @JsonProperty("user_type") String userType, - @JsonProperty("user_count") Integer userCount, - @JsonProperty("static_ip_score") Double staticIpScore - ) { - this.autonomousSystemNumber = autonomousSystemNumber; - this.autonomousSystemOrganization = autonomousSystemOrganization; - this.connectionType = connectionType; - this.domain = domain; - this.ipAddress = ipAddress; - this.isAnonymous = isAnonymous; - this.isAnonymousVpn = isAnonymousVpn; - this.isAnycast = isAnycast; - this.isHostingProvider = isHostingProvider; - this.isLegitimateProxy = isLegitimateProxy; - this.isPublicProxy = isPublicProxy; - this.isResidentialProxy = isResidentialProxy; - this.isTorExitNode = isTorExitNode; - this.isp = isp; - this.mobileCountryCode = mobileCountryCode; - this.mobileNetworkCode = mobileNetworkCode; - this.network = network; - this.organization = organization; - this.userType = userType; - this.userCount = userCount; - this.staticIpScore = staticIpScore; - } - - /** - * Constructs an instance of {@code Traits}. - * - * @param autonomousSystemNumber the autonomous system number - * @param autonomousSystemOrganization the autonomous system organization - * @param connectionType the connection type - * @param domain the domain - * @param ipAddress the IP address - * @param isAnonymous the anonymous flag - * @param isAnonymousVpn the anonymous VPN flag - * @param isAnycast the anycast flag - * @param isHostingProvider the hosting provider flag - * @param isLegitimateProxy the legitimate proxy flag - * @param isPublicProxy the public proxy flag - * @param isResidentialProxy the residential proxy flag - * @param isTorExitNode the Tor exit node flag - * @param isp the ISP - * @param mobileCountryCode the mobile country code - * @param mobileNetworkCode the mobile network code - * @param network the network - * @param organization the organization - * @param userType the user type - * @param userCount the user count - * @param staticIpScore the static IP score - */ @MaxMindDbConstructor public Traits( @MaxMindDbParameter(name = "autonomous_system_number") Long autonomousSystemNumber, @@ -183,33 +238,33 @@ public Traits( @MaxMindDbParameter(name = "user_count") Integer userCount, @MaxMindDbParameter(name = "static_ip_score") Double staticIpScore ) { - this.autonomousSystemNumber = autonomousSystemNumber; - this.autonomousSystemOrganization = autonomousSystemOrganization; - this.connectionType = ConnectionType.fromString(connectionType); - this.domain = domain; - this.ipAddress = ipAddress; - this.isAnonymous = isAnonymous != null ? isAnonymous : false; - this.isAnonymousVpn = isAnonymousVpn != null ? isAnonymousVpn : false; - this.isAnycast = isAnycast != null ? isAnycast : false; - this.isHostingProvider = isHostingProvider != null ? isHostingProvider : false; - this.isLegitimateProxy = isLegitimateProxy != null ? isLegitimateProxy : false; - this.isPublicProxy = isPublicProxy != null ? isPublicProxy : false; - this.isResidentialProxy = isResidentialProxy != null ? isResidentialProxy : false; - this.isTorExitNode = isTorExitNode != null ? isTorExitNode : false; - this.isp = isp; - this.mobileCountryCode = mobileCountryCode; - this.mobileNetworkCode = mobileNetworkCode; - this.network = network; - this.organization = organization; - this.userType = userType; - this.userCount = userCount; - this.staticIpScore = staticIpScore; + this( + autonomousSystemNumber, + autonomousSystemOrganization, + ConnectionType.fromString(connectionType), + domain, + ipAddress, + isAnonymous != null ? isAnonymous : false, + isAnonymousVpn != null ? isAnonymousVpn : false, + isAnycast != null ? isAnycast : false, + isHostingProvider != null ? isHostingProvider : false, + isLegitimateProxy != null ? isLegitimateProxy : false, + isPublicProxy != null ? isPublicProxy : false, + isResidentialProxy != null ? isResidentialProxy : false, + isTorExitNode != null ? isTorExitNode : false, + isp, + mobileCountryCode, + mobileNetworkCode, + network, + organization, + userType, + userCount, + staticIpScore + ); } - - /** - * Constructs an instance of {@code Traits}. + * Constructs an instance of {@code Traits}. * * @param traits the traits * @param ipAddress the IP address @@ -221,10 +276,10 @@ public Traits( Network network ) { this( - traits.getAutonomousSystemNumber(), - traits.getAutonomousSystemOrganization(), - traits.getConnectionType(), - traits.getDomain(), + traits.autonomousSystemNumber(), + traits.autonomousSystemOrganization(), + traits.connectionType(), + traits.domain(), ipAddress, traits.isAnonymous(), traits.isAnonymousVpn(), @@ -234,14 +289,14 @@ public Traits( traits.isPublicProxy(), traits.isResidentialProxy(), traits.isTorExitNode(), - traits.getIsp(), - traits.getMobileCountryCode(), - traits.getMobileNetworkCode(), + traits.isp(), + traits.mobileCountryCode(), + traits.mobileNetworkCode(), network, - traits.getOrganization(), - traits.getUserType(), - traits.getUserCount(), - traits.getStaticIpScore() + traits.organization(), + traits.userType(), + traits.userCount(), + traits.staticIpScore() ); } @@ -251,10 +306,13 @@ public Traits( * >autonomous system number associated with the IP address. This * is only available from the City Plus and Insights web services and * the Enterprise database. + * @deprecated Use {@link #autonomousSystemNumber()} instead. This method will be + * removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("autonomous_system_number") public Long getAutonomousSystemNumber() { - return this.autonomousSystemNumber; + return autonomousSystemNumber(); } /** @@ -263,30 +321,37 @@ public Long getAutonomousSystemNumber() { * >autonomous system number for the IP address. This is only * available from the City Plus and Insights web services and the * Enterprise database. + * @deprecated Use {@link #autonomousSystemOrganization()} instead. This method will be + * removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("autonomous_system_organization") public String getAutonomousSystemOrganization() { - return this.autonomousSystemOrganization; + return autonomousSystemOrganization(); } /** * @return The connection type of the IP address. This is only * available from the City Plus and Insights web services and the * Enterprise database. + * @deprecated Use {@link #connectionType()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("connection_type") public ConnectionType getConnectionType() { - return this.connectionType; + return connectionType(); } /** * @return The static IP score of the IP address. This is an indicator of * how static or dynamic an IP address is. This is only available from * the Insights web service. + * @deprecated Use {@link #staticIpScore()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("static_ip_score") public Double getStaticIpScore() { - return this.staticIpScore; + return staticIpScore(); } /** @@ -294,10 +359,12 @@ public Double getStaticIpScore() { * during the past 24 hours. For IPv4, the count is for the individual * IP address. For IPv6, the count is for the /64 network. This is only * available from the Insights web service. + * @deprecated Use {@link #userCount()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("user_count") public Integer getUserCount() { - return this.userCount; + return userCount(); } /** @@ -305,10 +372,12 @@ public Integer getUserCount() { * will be something like "example.com" or "example.co.uk", not * "foo.example.com". This is only available from the City Plus and * Insights web services and the Enterprise database. + * @deprecated Use {@link #domain()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty public String getDomain() { - return this.domain; + return domain(); } /** @@ -317,100 +386,23 @@ public String getDomain() { * externally routable IP address for the system the code is running * on. If the system is behind a NAT, this may differ from the IP * address locally assigned to it. + * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return this.ipAddress; + return ipAddress(); } /** * @return The name of the ISP associated with the IP address. This * is only available from the City Plus and Insights web services and * the Enterprise database. + * @deprecated Use {@link #isp()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) public String getIsp() { - return this.isp; - } - - /** - * @return This is true if the IP address belongs to any sort of anonymous - * network. This is only available from the Insights web service. - */ - @JsonProperty("is_anonymous") - public boolean isAnonymous() { - return this.isAnonymous; - } - - - /** - * @return This is true if the IP address is registered to an anonymous - * VPN provider. If a VPN provider does not register subnets under names - * associated with them, we will likely only flag their IP ranges using - * isHostingProvider. This is only available from the Insights web - * service. - */ - @JsonProperty("is_anonymous_vpn") - public boolean isAnonymousVpn() { - return this.isAnonymousVpn; - } - - /** - * @return This is true if the IP address belongs to an anycast network. - * This is not available from GeoLite databases or web services. - */ - @JsonProperty("is_anycast") - public boolean isAnycast() { - return this.isAnycast; - } - - /** - * @return This is true if the IP address belongs to a hosting or - * VPN provider (see description of isAnonymousVpn). This is only - * available from the Insights web service. - */ - @JsonProperty("is_hosting_provider") - public boolean isHostingProvider() { - return this.isHostingProvider; - } - - /** - * @return This is true if MaxMind believes this IP address to be a - * legitimate proxy, such as an internal VPN used by a corporation. This is - * only available in the Enterprise database. - */ - @JsonProperty("is_legitimate_proxy") - public boolean isLegitimateProxy() { - return this.isLegitimateProxy; - } - - /** - * @return This is true if the IP address belongs to a public proxy. - * This is only available from the Insights web service. - */ - @JsonProperty("is_public_proxy") - public boolean isPublicProxy() { - return this.isPublicProxy; - } - - /** - * @return This is true if the IP address is on a suspected anonymizing - * network and belongs to a residential ISP. This is only available from - * the Insights web service. - */ - @JsonProperty("is_residential_proxy") - public boolean isResidentialProxy() { - return this.isResidentialProxy; - } - - - /** - * @return This is true if the IP address belongs to a Tor exit node. - * This is only available from the Insights web service. - */ - @JsonProperty("is_tor_exit_node") - public boolean isTorExitNode() { - return this.isTorExitNode; + return isp(); } /** @@ -418,10 +410,12 @@ public boolean isTorExitNode() { * mobile country code (MCC) associated with the IP address and ISP. * This is available from the City Plus and Insights web services and the * Enterprise database. + * @deprecated Use {@link #mobileCountryCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("mobile_country_code") public String getMobileCountryCode() { - return this.mobileCountryCode; + return mobileCountryCode(); } /** @@ -429,31 +423,37 @@ public String getMobileCountryCode() { * mobile network code (MNC) associated with the IP address and ISP. * This is available from the City Plus and Insights web services and the * Enterprise database. + * @deprecated Use {@link #mobileNetworkCode()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("mobile_network_code") public String getMobileNetworkCode() { - return this.mobileNetworkCode; + return mobileNetworkCode(); } /** * @return The network associated with the record. In particular, this is * the largest network where all the fields besides IP address have the * same value. + * @deprecated Use {@link #network()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty @JsonSerialize(using = ToStringSerializer.class) public Network getNetwork() { - return this.network; + return network(); } /** * @return The name of the organization associated with the IP address. * This is only available from the City Plus and Insights web services and * the Enterprise database. + * @deprecated Use {@link #organization()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty public String getOrganization() { - return this.organization; + return organization(); } /** @@ -483,9 +483,11 @@ public String getOrganization() { * This is only available from the Insights web service and the Enterprise * database. *

+ * @deprecated Use {@link #userType()} instead. This method will be removed in 6.0.0. */ + @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("user_type") public String getUserType() { - return this.userType; + return userType(); } } diff --git a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java index e70b3e05d..a3e22e2df 100644 --- a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java +++ b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java @@ -63,7 +63,7 @@ public void testDefaultLocaleURL() throws Exception { private void testDefaultLocale(DatabaseReader reader) throws IOException, GeoIp2Exception { CityResponse city = reader.city(InetAddress.getByName("81.2.69.160")); - assertEquals("London", city.getCity().getName()); + assertEquals("London", city.city().name()); } @Test @@ -72,8 +72,8 @@ public void testIsInEuropeanUnion() throws Exception { .build() ) { CityResponse city = reader.city(InetAddress.getByName("89.160.20.128")); - assertTrue(city.getCountry().isInEuropeanUnion()); - assertTrue(city.getRegisteredCountry().isInEuropeanUnion()); + assertTrue(city.country().isInEuropeanUnion()); + assertTrue(city.registeredCountry().isInEuropeanUnion()); } } @@ -100,7 +100,7 @@ public void testLocaleListURL() throws Exception { private void testLocaleList(DatabaseReader reader) throws IOException, GeoIp2Exception { CityResponse city = reader.city(InetAddress.getByName("81.2.69.160")); - assertEquals("Лондон", city.getCity().getName()); + assertEquals("Лондон", city.city().name()); } @Test @@ -124,15 +124,15 @@ public void testMemoryModeURL() throws Exception { private void testMemoryMode(DatabaseReader reader) throws IOException, GeoIp2Exception { CityResponse city = reader.city(InetAddress.getByName("81.2.69.160")); - assertEquals("London", city.getCity().getName()); - assertEquals(100, city.getLocation().getAccuracyRadius().longValue()); + assertEquals("London", city.city().name()); + assertEquals(100, city.location().accuracyRadius().longValue()); } @Test public void metadata() throws IOException { DatabaseReader reader = new DatabaseReader.Builder(this.geoipFile) .fileMode(Reader.FileMode.MEMORY).build(); - assertEquals("GeoIP2-City", reader.getMetadata().getDatabaseType()); + assertEquals("GeoIP2-City", reader.metadata().getDatabaseType()); } @Test @@ -156,8 +156,8 @@ public void hasIpAddressURL() throws Exception { private void hasIpInfo(DatabaseReader reader) throws IOException, GeoIp2Exception { CityResponse cio = reader.city(InetAddress.getByName("81.2.69.160")); - assertEquals("81.2.69.160", cio.getTraits().getIpAddress()); - assertEquals("81.2.69.160/27", cio.getTraits().getNetwork().toString()); + assertEquals("81.2.69.160", cio.traits().ipAddress()); + assertEquals("81.2.69.160/27", cio.traits().network().toString()); } @Test @@ -220,8 +220,8 @@ public void testAnonymousIp() throws Exception { assertFalse(response.isPublicProxy()); assertFalse(response.isResidentialProxy()); assertFalse(response.isTorExitNode()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.2.0.0/16", response.getNetwork().toString()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.2.0.0/16", response.network().toString()); AnonymousIpResponse tryResponse = reader.tryAnonymousIp(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); @@ -235,17 +235,17 @@ public void testAnonymousPlus() throws Exception { ) { InetAddress ipAddress = InetAddress.getByName("1.2.0.1"); AnonymousPlusResponse response = reader.anonymousPlus(ipAddress); - assertEquals(30, response.getAnonymizerConfidence()); + assertEquals(30, response.anonymizerConfidence()); assertTrue(response.isAnonymous()); assertTrue(response.isAnonymousVpn()); assertFalse(response.isHostingProvider()); assertFalse(response.isPublicProxy()); assertFalse(response.isResidentialProxy()); assertFalse(response.isTorExitNode()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.2.0.1/32", response.getNetwork().toString()); - assertEquals("2025-04-14", response.getNetworkLastSeen().toString()); - assertEquals("foo", response.getProviderName()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.2.0.1/32", response.network().toString()); + assertEquals("2025-04-14", response.networkLastSeen().toString()); + assertEquals("foo", response.providerName()); AnonymousPlusResponse tryResponse = reader.tryAnonymousPlus(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); @@ -270,11 +270,11 @@ public void testAsn() throws Exception { ) { InetAddress ipAddress = InetAddress.getByName("1.128.0.0"); AsnResponse response = reader.asn(ipAddress); - assertEquals(1221, response.getAutonomousSystemNumber().intValue()); + assertEquals(1221, response.autonomousSystemNumber().intValue()); assertEquals("Telstra Pty Ltd", - response.getAutonomousSystemOrganization()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.128.0.0/11", response.getNetwork().toString()); + response.autonomousSystemOrganization()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.128.0.0/11", response.network().toString()); AsnResponse tryResponse = reader.tryAsn(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); @@ -289,18 +289,18 @@ public void testCity() throws Exception { InetAddress ipAddress = InetAddress.getByName("81.2.69.192"); CityResponse response = reader.city(ipAddress); - assertEquals(2635167, response.getCountry().getGeoNameId().intValue()); - assertEquals(100, response.getLocation().getAccuracyRadius().intValue()); - assertFalse(response.getTraits().isLegitimateProxy()); - assertEquals(ipAddress.getHostAddress(), response.getTraits().getIpAddress()); - assertEquals("81.2.69.192/28", response.getTraits().getNetwork().toString()); + assertEquals(2635167, response.country().geonameId().intValue()); + assertEquals(100, response.location().accuracyRadius().intValue()); + assertFalse(response.traits().isLegitimateProxy()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals("81.2.69.192/28", response.traits().network().toString()); CityResponse tryResponse = reader.tryCity(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); // This IP has is_anycast response = reader.city(InetAddress.getByName("214.1.1.0")); - assertTrue(response.getTraits().isAnycast()); + assertTrue(response.traits().isAnycast()); // Test that the methods can be called on DB without // an exception @@ -317,9 +317,9 @@ public void testConnectionType() throws Exception { ConnectionTypeResponse response = reader.connectionType(ipAddress); - assertEquals(ConnectionType.CELLULAR, response.getConnectionType()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.0.1.0/24", response.getNetwork().toString()); + assertEquals(ConnectionType.CELLULAR, response.connectionType()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.0.1.0/24", response.network().toString()); ConnectionTypeResponse tryResponse = reader.tryConnectionType(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); @@ -334,18 +334,18 @@ public void testCountry() throws Exception { InetAddress ipAddress = InetAddress.getByName("74.209.24.0"); CountryResponse response = reader.country(ipAddress); - assertEquals("NA", response.getContinent().getCode()); - assertEquals(6252001, response.getCountry().getGeoNameId().intValue()); - assertEquals(6252001, response.getRegisteredCountry().getGeoNameId().intValue()); - assertEquals(ipAddress.getHostAddress(), response.getTraits().getIpAddress()); - assertEquals("74.209.16.0/20", response.getTraits().getNetwork().toString()); + assertEquals("NA", response.continent().code()); + assertEquals(6252001, response.country().geonameId().intValue()); + assertEquals(6252001, response.registeredCountry().geonameId().intValue()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals("74.209.16.0/20", response.traits().network().toString()); CountryResponse tryResponse = reader.tryCountry(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); // This IP has is_anycast response = reader.country(InetAddress.getByName("214.1.1.0")); - assertTrue(response.getTraits().isAnycast()); + assertTrue(response.traits().isAnycast()); } } @@ -356,9 +356,9 @@ public void testDomain() throws Exception { ) { InetAddress ipAddress = InetAddress.getByName("1.2.0.0"); DomainResponse response = reader.domain(ipAddress); - assertEquals("maxmind.com", response.getDomain()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.2.0.0/16", response.getNetwork().toString()); + assertEquals("maxmind.com", response.domain()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.2.0.0/16", response.network().toString()); DomainResponse tryResponse = reader.tryDomain(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); @@ -373,26 +373,26 @@ public void testEnterprise() throws Exception { InetAddress ipAddress = InetAddress.getByName("74.209.24.0"); EnterpriseResponse response = reader.enterprise(ipAddress); - assertEquals(11, response.getCity().getConfidence().intValue()); - assertEquals(99, response.getCountry().getConfidence().intValue()); - assertEquals(6252001, response.getCountry().getGeoNameId().intValue()); - assertEquals(27, response.getLocation().getAccuracyRadius().intValue()); - assertEquals(ConnectionType.CABLE_DSL, response.getTraits().getConnectionType()); - assertTrue(response.getTraits().isLegitimateProxy()); - assertEquals(ipAddress.getHostAddress(), response.getTraits().getIpAddress()); - assertEquals("74.209.16.0/20", response.getTraits().getNetwork().toString()); + assertEquals(11, response.city().confidence().intValue()); + assertEquals(99, response.country().confidence().intValue()); + assertEquals(6252001, response.country().geonameId().intValue()); + assertEquals(27, response.location().accuracyRadius().intValue()); + assertEquals(ConnectionType.CABLE_DSL, response.traits().connectionType()); + assertTrue(response.traits().isLegitimateProxy()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals("74.209.16.0/20", response.traits().network().toString()); EnterpriseResponse tryResponse = reader.tryEnterprise(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); ipAddress = InetAddress.getByName("149.101.100.0"); response = reader.enterprise(ipAddress); - assertEquals("310", response.getTraits().getMobileCountryCode()); - assertEquals("004", response.getTraits().getMobileNetworkCode()); + assertEquals("310", response.traits().mobileCountryCode()); + assertEquals("004", response.traits().mobileNetworkCode()); // This IP has is_anycast response = reader.enterprise(InetAddress.getByName("214.1.1.0")); - assertTrue(response.getTraits().isAnycast()); + assertTrue(response.traits().isAnycast()); // Test that the city and country methods can be called without // an exception @@ -408,22 +408,22 @@ public void testIsp() throws Exception { ) { InetAddress ipAddress = InetAddress.getByName("1.128.0.0"); IspResponse response = reader.isp(ipAddress); - assertEquals(1221, response.getAutonomousSystemNumber().intValue()); + assertEquals(1221, response.autonomousSystemNumber().intValue()); assertEquals("Telstra Pty Ltd", - response.getAutonomousSystemOrganization()); - assertEquals("Telstra Internet", response.getIsp()); - assertEquals("Telstra Internet", response.getOrganization()); + response.autonomousSystemOrganization()); + assertEquals("Telstra Internet", response.isp()); + assertEquals("Telstra Internet", response.organization()); - assertEquals(ipAddress.getHostAddress(), response.getIpAddress()); - assertEquals("1.128.0.0/11", response.getNetwork().toString()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals("1.128.0.0/11", response.network().toString()); IspResponse tryResponse = reader.tryIsp(ipAddress).get(); assertEquals(response.toJson(), tryResponse.toJson()); ipAddress = InetAddress.getByName("149.101.100.0"); response = reader.isp(ipAddress); - assertEquals("310", response.getMobileCountryCode()); - assertEquals("004", response.getMobileNetworkCode()); + assertEquals("310", response.mobileCountryCode()); + assertEquals("004", response.mobileNetworkCode()); } } diff --git a/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java b/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java index 035003819..4e0f49546 100644 --- a/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java +++ b/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java @@ -25,7 +25,6 @@ import com.maxmind.geoip2.exception.OutOfQueriesException; import com.maxmind.geoip2.exception.PermissionRequiredException; import com.maxmind.geoip2.model.InsightsResponse; -import com.maxmind.geoip2.record.AbstractNamedRecord; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; @@ -71,7 +70,6 @@ public void test200WithInvalidJson() throws Exception { assertEquals("Received a 200 response but could not decode it as JSON", ex.getMessage()); } - @SuppressWarnings("deprecation") @Test public void test200WithDefaultValues() throws Exception { WebServiceClient client = createSuccessClient("insights", "1.2.3.13", @@ -81,93 +79,98 @@ public void test200WithDefaultValues() throws Exception { .getByName("1.2.3.13")); assertThat(insights.toString(), - CoreMatchers.startsWith("com.maxmind.geoip2.model.InsightsResponse [ {")); + CoreMatchers.startsWith("InsightsResponse[")); - City city = insights.getCity(); + City city = insights.city(); assertNotNull(city); - assertNull(city.getConfidence()); + assertNull(city.confidence()); - Continent continent = insights.getContinent(); + Continent continent = insights.continent(); assertNotNull(continent); - assertNull(continent.getCode()); + assertNull(continent.code()); - Country country = insights.getCountry(); + Country country = insights.country(); assertNotNull(country); - Location location = insights.getLocation(); + Location location = insights.location(); assertNotNull(location); - assertNull(location.getAccuracyRadius()); - assertNull(location.getLatitude()); - assertNull(location.getLongitude()); - assertNull(location.getTimeZone()); + assertNull(location.accuracyRadius()); + assertNull(location.latitude()); + assertNull(location.longitude()); + assertNull(location.timeZone()); assertThat(location.toString(), - CoreMatchers.equalTo("com.maxmind.geoip2.record.Location [ {} ]")); + CoreMatchers.startsWith("Location[")); - MaxMind maxmind = insights.getMaxMind(); + MaxMind maxmind = insights.maxmind(); assertNotNull(maxmind); - assertNull(maxmind.getQueriesRemaining()); + assertNull(maxmind.queriesRemaining()); - assertNotNull(insights.getPostal()); + assertNotNull(insights.postal()); - Country registeredCountry = insights.getRegisteredCountry(); + Country registeredCountry = insights.registeredCountry(); assertNotNull(registeredCountry); RepresentedCountry representedCountry = insights - .getRepresentedCountry(); + .representedCountry(); assertNotNull(representedCountry); - assertNull(representedCountry.getType()); + assertNull(representedCountry.type()); - List subdivisions = insights.getSubdivisions(); + List subdivisions = insights.subdivisions(); assertNotNull(subdivisions); assertTrue(subdivisions.isEmpty()); - Subdivision subdiv = insights.getMostSpecificSubdivision(); + Subdivision subdiv = insights.mostSpecificSubdivision(); assertNotNull(subdiv); - assertNull(subdiv.getIsoCode()); - assertNull(subdiv.getConfidence()); + assertNull(subdiv.isoCode()); + assertNull(subdiv.confidence()); - Subdivision leastSpecificSubdiv = insights.getLeastSpecificSubdivision(); + Subdivision leastSpecificSubdiv = insights.leastSpecificSubdivision(); assertNotNull(leastSpecificSubdiv); - assertNull(leastSpecificSubdiv.getIsoCode()); - assertNull(leastSpecificSubdiv.getConfidence()); + assertNull(leastSpecificSubdiv.isoCode()); + assertNull(leastSpecificSubdiv.confidence()); - Traits traits = insights.getTraits(); + Traits traits = insights.traits(); assertNotNull(traits); - assertNull(traits.getAutonomousSystemNumber()); - assertNull(traits.getAutonomousSystemOrganization()); - assertNull(traits.getConnectionType()); - assertNull(traits.getDomain()); - assertEquals("1.2.3.13", traits.getIpAddress()); - assertEquals("1.2.3.0/24", traits.getNetwork().toString()); - assertNull(traits.getIsp()); - assertNull(traits.getOrganization()); - assertNull(traits.getUserType()); - assertNull(traits.getStaticIpScore()); - assertNull(traits.getUserCount()); + assertNull(traits.autonomousSystemNumber()); + assertNull(traits.autonomousSystemOrganization()); + assertNull(traits.connectionType()); + assertNull(traits.domain()); + assertEquals("1.2.3.13", traits.ipAddress()); + assertEquals("1.2.3.0/24", traits.network().toString()); + assertNull(traits.isp()); + assertNull(traits.organization()); + assertNull(traits.userType()); + assertNull(traits.staticIpScore()); + assertNull(traits.userCount()); assertFalse(traits.isAnycast()); - for (Country c : new Country[] {country, registeredCountry, - representedCountry}) { - assertNull(c.getConfidence()); - assertNull(c.getIsoCode()); + for (Country c : new Country[] {country, registeredCountry}) { + assertNull(c.confidence()); + assertNull(c.isoCode()); assertFalse(c.isInEuropeanUnion()); } - for (AbstractNamedRecord r : new AbstractNamedRecord[] {city, + // Check RepresentedCountry separately since it's no longer a Country + assertNull(representedCountry.confidence()); + assertNull(representedCountry.isoCode()); + assertFalse(representedCountry.isInEuropeanUnion()); + + for (NamedRecord r : new NamedRecord[] {city, continent, subdiv}) { - assertNull(r.getGeoNameId()); - assertNull(r.getName()); - assertTrue(r.getNames().isEmpty()); - assertEquals(r.getClass().getName() + " [ {} ]", r.toString()); + assertNull(r.geonameId()); + assertNull(r.name()); + assertTrue(r.names().isEmpty()); + // Records have their own toString format + assertNotNull(r.toString()); } - for (AbstractNamedRecord r : new AbstractNamedRecord[] {country, + for (NamedRecord r : new NamedRecord[] {country, registeredCountry, representedCountry}) { - assertNull(r.getGeoNameId()); - assertNull(r.getName()); - assertTrue(r.getNames().isEmpty()); - assertEquals(r.getClass().getName() + - " [ {\"is_in_european_union\":false} ]", r.toString()); + assertNull(r.geonameId()); + assertNull(r.name()); + assertTrue(r.names().isEmpty()); + // Records have their own toString format + assertNotNull(r.toString()); } } @@ -176,7 +179,7 @@ public void test200OnInsightsAsMe() throws Exception { WebServiceClient client = createSuccessClient("insights", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.insights().getTraits().getIpAddress()); + client.insights().traits().ipAddress()); } @Test @@ -184,7 +187,7 @@ public void test200OnCityAsMe() throws Exception { WebServiceClient client = createSuccessClient("city", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.city().getTraits().getIpAddress()); + client.city().traits().ipAddress()); } @Test @@ -192,7 +195,7 @@ public void test200OnCountryAsMe() throws Exception { WebServiceClient client = createSuccessClient("country", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.country().getTraits().getIpAddress()); + client.country().traits().ipAddress()); } @Test diff --git a/src/test/java/com/maxmind/geoip2/matchers/CodeMatcher.java b/src/test/java/com/maxmind/geoip2/matchers/CodeMatcher.java index 9b1cb00fa..bef6fe21b 100644 --- a/src/test/java/com/maxmind/geoip2/matchers/CodeMatcher.java +++ b/src/test/java/com/maxmind/geoip2/matchers/CodeMatcher.java @@ -19,7 +19,7 @@ private CodeMatcher(String expectedErrorCode) { @Override protected boolean matchesSafely(final InvalidRequestException exception) { - this.foundErrorCode = exception.getCode(); + this.foundErrorCode = exception.code(); return this.foundErrorCode.equalsIgnoreCase(this.expectedErrorCode); } diff --git a/src/test/java/com/maxmind/geoip2/matchers/HttpStatusMatcher.java b/src/test/java/com/maxmind/geoip2/matchers/HttpStatusMatcher.java index 9704e309a..8d192138a 100644 --- a/src/test/java/com/maxmind/geoip2/matchers/HttpStatusMatcher.java +++ b/src/test/java/com/maxmind/geoip2/matchers/HttpStatusMatcher.java @@ -19,7 +19,7 @@ private HttpStatusMatcher(int expectedStatusCode) { @Override protected boolean matchesSafely(final HttpException exception) { - this.foundStatusCode = exception.getHttpStatus(); + this.foundStatusCode = exception.httpStatus(); return this.foundStatusCode == this.expectedStatusCode; } diff --git a/src/test/java/com/maxmind/geoip2/model/CityResponseTest.java b/src/test/java/com/maxmind/geoip2/model/CityResponseTest.java index 5b080e733..a9dce2a62 100644 --- a/src/test/java/com/maxmind/geoip2/model/CityResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/CityResponseTest.java @@ -55,17 +55,17 @@ public void testNames() throws Exception { CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); assertEquals( "北美洲", - city.getContinent().getName(), - "country.getContinent().getName() does not return 北美洲" + city.continent().name(), + "country.continent().name() does not return 北美洲" ); assertEquals( "美国", - city.getCountry().getName(), - "country.getCountry().getName() does not return 美国" + city.country().name(), + "country.country().name() does not return 美国" ); assertEquals( - city.getCountry() - .getName(), city.getCountry().getName(), + city.country() + .name(), city.country().name(), "toString() returns getName()" ); } @@ -82,8 +82,8 @@ public void russianFallback() throws Exception { CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); assertEquals( "объединяет государства", - city.getCountry().getName(), - "country.getCountry().getName() does not return объединяет государства" + city.country().name(), + "country.country().name() does not return объединяет государства" ); } @@ -99,7 +99,7 @@ public void testFallback() throws Exception { CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); assertEquals( "North America", - city.getContinent().getName(), + city.continent().name(), "en is returned when pt is missing" ); @@ -116,7 +116,7 @@ public void noFallback() throws Exception { CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); assertNull( - city.getContinent().getName(), + city.continent().name(), "null is returned when locale is not available" ); } @@ -132,7 +132,7 @@ public void noLocale() throws Exception { CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); assertEquals( "North America", - city.getContinent().getName(), + city.continent().name(), "en is returned when no locales are specified" ); @@ -148,7 +148,7 @@ public void testMissing() throws Exception { .locales(Collections.singletonList("en")).build(); CityResponse city = client.city(InetAddress.getByName("1.1.1.2")); - assertNotNull(city.getCity()); - assertNull(city.getCity().getName(), "null is returned when names object is missing"); + assertNotNull(city.city()); + assertNull(city.city().name(), "null is returned when names object is missing"); } } diff --git a/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java b/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java index 9535f0778..a60a2b0c1 100644 --- a/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java @@ -52,97 +52,97 @@ public void createClient() throws IOException, GeoIp2Exception, public void testContinent() { assertEquals( "NA", - this.country.getContinent().getCode(), - "country.getContinent().getCode() does not return NA" + this.country.continent().code(), + "country.continent().code() does not return NA" ); assertEquals( 42, - this.country.getContinent().getGeoNameId(), - "country.getContinent().getGeoNameId() does not return 42" + this.country.continent().geonameId(), + "country.continent().geonameId() does not return 42" ); assertEquals( "North America", - this.country.getContinent().getName(), - "country.getContinent().getName() does not return North America" + this.country.continent().name(), + "country.continent().name() does not return North America" ); } @Test public void testCountry() { assertFalse( - this.country.getCountry().isInEuropeanUnion(), - "country.getCountry().isInEuropeanUnion() does not return false" + this.country.country().isInEuropeanUnion(), + "country.country().isInEuropeanUnion() does not return false" ); assertEquals( - this.country.getCountry().getIsoCode(), + this.country.country().isoCode(), "US", - "country.getCountry().getCode() does not return US" + "country.country().code() does not return US" ); assertEquals( 1, - (long) this.country.getCountry().getGeoNameId(), - "country.getCountry().getGeoNameId() does not return 1" + (long) this.country.country().geonameId(), + "country.country().geonameId() does not return 1" ); assertEquals( Integer.valueOf(56), - this.country.getCountry().getConfidence(), - "country.getCountry().getConfidence() does not return 56" + this.country.country().confidence(), + "country.country().confidence() does not return 56" ); assertEquals( "United States", - this.country.getCountry().getName(), - "country.getCountry().getName(\"en\") does not return United States" + this.country.country().name(), + "country.country().name(\"en\") does not return United States" ); } @Test public void testRegisteredCountry() { assertFalse( - this.country.getRegisteredCountry().isInEuropeanUnion(), - "country.getRegisteredCountry().isInEuropeanUnion() does not return false" + this.country.registeredCountry().isInEuropeanUnion(), + "country.registeredCountry().isInEuropeanUnion() does not return false" ); assertEquals( "CA", - this.country.getRegisteredCountry().getIsoCode(), - "country.getRegisteredCountry().getIsoCode() does not return CA" + this.country.registeredCountry().isoCode(), + "country.registeredCountry().isoCode() does not return CA" ); assertEquals( 2, - (long) this.country.getRegisteredCountry().getGeoNameId(), - "country.getRegisteredCountry().getGeoNameId() does not return 2" + (long) this.country.registeredCountry().geonameId(), + "country.registeredCountry().geonameId() does not return 2" ); assertEquals( "Canada", - this.country.getRegisteredCountry().getName(), - "country.getRegisteredCountry().getName(\"en\") does not return United States" + this.country.registeredCountry().name(), + "country.registeredCountry().name(\"en\") does not return United States" ); } @Test public void testRepresentedCountry() { assertTrue( - this.country.getRepresentedCountry().isInEuropeanUnion(), - "country.getRepresentedCountry().isInEuropeanUnion() does not return true" + this.country.representedCountry().isInEuropeanUnion(), + "country.representedCountry().isInEuropeanUnion() does not return true" ); assertEquals( "GB", - this.country.getRepresentedCountry().getIsoCode(), - "country.getRepresentedCountry().getCode() does not return GB" + this.country.representedCountry().isoCode(), + "country.representedCountry().code() does not return GB" ); assertEquals( 4, - (long) this.country.getRepresentedCountry().getGeoNameId(), - "country.getRepresentedCountry().getGeoNameId() does not return 4" + (long) this.country.representedCountry().geonameId(), + "country.representedCountry().geonameId() does not return 4" ); assertEquals( "United Kingdom", - this.country.getRepresentedCountry().getName(), - "country.getRepresentedCountry().getName(\"en\") does not return United Kingdom" + this.country.representedCountry().name(), + "country.representedCountry().name(\"en\") does not return United Kingdom" ); assertEquals( "military", - this.country.getRepresentedCountry().getType(), - "country.getRepresentedCountry().getType() does not return military" + this.country.representedCountry().type(), + "country.representedCountry().type() does not return military" ); } @@ -151,8 +151,8 @@ public void testTraits() { assertEquals( "1.2.3.4", - this.country.getTraits().getIpAddress(), - "country.getTraits().getIpAddress does not return 1.2.3.4" + this.country.traits().ipAddress(), + "country.traits().getIpAddress does not return 1.2.3.4" ); } diff --git a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java index 34dab71e7..ccb1716ef 100644 --- a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java @@ -64,7 +64,7 @@ public void createClient() throws IOException, GeoIp2Exception, @Test public void testSubdivisionsList() { - List subdivisionsList = this.insights.getSubdivisions(); + List subdivisionsList = this.insights.subdivisions(); assertNotNull(subdivisionsList, "city.getSubdivisionsList returns null"); if (subdivisionsList.isEmpty()) { fail("subdivisionsList is empty"); @@ -72,18 +72,18 @@ public void testSubdivisionsList() { Subdivision subdivision = subdivisionsList.get(0); assertEquals( Integer.valueOf(88), - subdivision.getConfidence(), - "subdivision.getConfidence() does not return 88" + subdivision.confidence(), + "subdivision.confidence() does not return 88" ); assertEquals( 574635, - subdivision.getGeoNameId().intValue(), - "subdivision.getGeoNameId() does not return 574635" + subdivision.geonameId().intValue(), + "subdivision.geonameId() does not return 574635" ); assertEquals( "MN", - subdivision.getIsoCode(), - "subdivision.getCode() does not return MN" + subdivision.isoCode(), + "subdivision.code() does not return MN" ); } @@ -91,7 +91,7 @@ public void testSubdivisionsList() { public void mostSpecificSubdivision() { assertEquals( "TT", - this.insights.getMostSpecificSubdivision().getIsoCode(), + this.insights.mostSpecificSubdivision().isoCode(), "Most specific subdivision returns last subdivision" ); } @@ -100,43 +100,42 @@ public void mostSpecificSubdivision() { public void leastSpecificSubdivision() { assertEquals( "MN", - this.insights.getLeastSpecificSubdivision().getIsoCode(), + this.insights.leastSpecificSubdivision().isoCode(), "Most specific subdivision returns first subdivision" ); } - @SuppressWarnings("deprecation") @Test public void testTraits() { - Traits traits = this.insights.getTraits(); + Traits traits = this.insights.traits(); - assertNotNull(traits, "city.getTraits() returns null"); + assertNotNull(traits, "city.traits() returns null"); assertEquals( Long.valueOf(1234), - traits.getAutonomousSystemNumber(), - "traits.getAutonomousSystemNumber() does not return 1234" + traits.autonomousSystemNumber(), + "traits.autonomousSystemNumber() does not return 1234" ); assertEquals( "AS Organization", - traits.getAutonomousSystemOrganization(), - "traits.getAutonomousSystemOrganization() does not return AS Organization" + traits.autonomousSystemOrganization(), + "traits.autonomousSystemOrganization() does not return AS Organization" ); assertEquals( ConnectionType.CABLE_DSL, - traits.getConnectionType(), - "traits.getConnectionType() does not return Cable/DSL" + traits.connectionType(), + "traits.connectionType() does not return Cable/DSL" ); assertEquals( "example.com", - traits.getDomain(), - "traits.getDomain() does not return example.com" + traits.domain(), + "traits.domain() does not return example.com" ); assertEquals( "1.2.3.4", - traits.getIpAddress(), - "traits.getIpAddress() does not return 1.2.3.4" + traits.ipAddress(), + "traits.ipAddress() does not return 1.2.3.4" ); assertTrue(traits.isAnonymous(), "traits.isAnonymous() returns true"); assertTrue(traits.isAnonymousVpn(), "traits.isAnonymousVpn() returns true"); @@ -146,83 +145,82 @@ public void testTraits() { assertTrue(traits.isTorExitNode(), "traits.isTorExitNode() returns true"); assertEquals( "Comcast", - traits.getIsp(), - "traits.getIsp() does not return Comcast" + traits.isp(), + "traits.isp() does not return Comcast" ); assertEquals( "Blorg", - traits.getOrganization(), - "traits.getOrganization() does not return Blorg" + traits.organization(), + "traits.organization() does not return Blorg" ); assertEquals( "college", - traits.getUserType(), - "traits.getUserType() does not return userType" + traits.userType(), + "traits.userType() does not return userType" ); assertEquals( Double.valueOf(1.3), - traits.getStaticIpScore(), - "traits.getStaticIpScore() does not return 1.3" + traits.staticIpScore(), + "traits.staticIpScore() does not return 1.3" ); assertEquals( Integer.valueOf(2), - traits.getUserCount(), - "traits.getUserCount() does not return 2" + traits.userCount(), + "traits.userCount() does not return 2" ); } - @SuppressWarnings("deprecation") @Test public void testLocation() { - Location location = this.insights.getLocation(); + Location location = this.insights.location(); - assertNotNull(location, "city.getLocation() returns null"); + assertNotNull(location, "city.location() returns null"); assertEquals( Integer.valueOf(24626), - location.getAverageIncome(), - "location.getAverageIncome() does not return 24626" + location.averageIncome(), + "location.averageIncome() does not return 24626" ); assertEquals( Integer.valueOf(1500), - location.getAccuracyRadius(), - "location.getAccuracyRadius() does not return 1500" + location.accuracyRadius(), + "location.accuracyRadius() does not return 1500" ); - double latitude = location.getLatitude(); + double latitude = location.latitude(); assertEquals( 44.98, latitude, 0.1, - "location.getLatitude() does not return 44.98" + "location.latitude() does not return 44.98" ); - double longitude = location.getLongitude(); + double longitude = location.longitude(); assertEquals( 93.2636, longitude, 0.1, - "location.getLongitude() does not return 93.2636" + "location.longitude() does not return 93.2636" ); assertEquals( Integer.valueOf(1341), - location.getPopulationDensity(), - "location.getPopulationDensity() does not return 1341" + location.populationDensity(), + "location.populationDensity() does not return 1341" ); assertEquals( "America/Chicago", - location.getTimeZone(), - "location.getTimeZone() does not return America/Chicago" + location.timeZone(), + "location.timeZone() does not return America/Chicago" ); } @Test public void testMaxMind() { - MaxMind maxmind = this.insights.getMaxMind(); + MaxMind maxmind = this.insights.maxmind(); assertEquals( 11, maxmind - .getQueriesRemaining().intValue(), + .queriesRemaining().intValue(), "Correct number of queries remaining" ); } @@ -230,34 +228,34 @@ public void testMaxMind() { @Test public void testPostal() { - Postal postal = this.insights.getPostal(); + Postal postal = this.insights.postal(); assertEquals( "55401", - postal.getCode(), - "postal.getCode() does not return 55401" + postal.code(), + "postal.code() does not return 55401" ); assertEquals( Integer.valueOf(33), - postal.getConfidence(), - "postal.getConfidence() does not return 33" + postal.confidence(), + "postal.confidence() does not return 33" ); } @Test public void testRepresentedCountry() { assertNotNull( - this.insights.getRepresentedCountry(), - "city.getRepresentedCountry() returns null" + this.insights.representedCountry(), + "city.representedCountry() returns null" ); assertEquals( "C", - this.insights.getRepresentedCountry().getType(), - "city.getRepresentedCountry().getType() does not return C" + this.insights.representedCountry().type(), + "city.representedCountry().type() does not return C" ); assertTrue( - this.insights.getRepresentedCountry().isInEuropeanUnion(), - "city.getRepresentedCountry().isInEuropeanUnion() does not return true" + this.insights.representedCountry().isInEuropeanUnion(), + "city.representedCountry().isInEuropeanUnion() does not return true" ); } @@ -276,11 +274,11 @@ public void testIsInEuropeanUnion() throws IOException, GeoIp2Exception { InetAddress.getByName("1.1.1.2")); assertTrue( - insights.getCountry().isInEuropeanUnion(), + insights.country().isInEuropeanUnion(), "getCountry().isInEuropeanUnion() does not return true" ); assertTrue( - insights.getRegisteredCountry().isInEuropeanUnion(), + insights.registeredCountry().isInEuropeanUnion(), "getRegisteredCountry().() isInEuropeanUnion = does not return true" ); } diff --git a/src/test/java/com/maxmind/geoip2/model/JsonTest.java b/src/test/java/com/maxmind/geoip2/model/JsonTest.java index 1a59fce6e..cd8109e52 100644 --- a/src/test/java/com/maxmind/geoip2/model/JsonTest.java +++ b/src/test/java/com/maxmind/geoip2/model/JsonTest.java @@ -329,7 +329,7 @@ public void testIspSerialization() throws Exception { testRoundTrip(IspResponse.class, json); } - protected void testRoundTrip + protected void testRoundTrip (Class cls, String json) throws IOException { JsonMapper mapper = JsonMapper.builder() From 4ebf177d86594d5854b4882ceb348a75b3eb96f1 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 14 Oct 2025 08:04:40 -0700 Subject: [PATCH 02/17] Deprecate get* methods in non-record classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Public getter methods in non-record classes (DatabaseReader, exception classes) have been renamed to follow the same naming convention as records (e.g., metadata() instead of getMetadata()). The old getter methods are still available but have been deprecated and will be removed in version 6.0.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 5 +++++ .../com/maxmind/geoip2/DatabaseReader.java | 15 ++++++++++--- .../geoip2/exception/HttpException.java | 22 +++++++++++++++++-- .../exception/InvalidRequestException.java | 22 +++++++++++++++++-- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 258b83c68..f182dbbef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,11 @@ CHANGELOG * **BREAKING:** Removed no longer necessary `JacksonInject` annotations for `ip_address`, `network`, and `traits` from several classes. The `JsonInjector` class was removed. +* Public getter methods in non-record classes (e.g., `DatabaseReader`, + exception classes) have been renamed to follow the same naming convention as + records (e.g., `metadata()` instead of `getMetadata()`). The old getter + methods are still available but have been deprecated and will be removed in + version 6.0.0. 4.4.0 (2025-08-28) ------------------ diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index 1c220e1c1..6a88283d8 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -115,7 +115,7 @@ private DatabaseReader(Builder builder) throws IOException { } private int getDatabaseType() { - String databaseType = this.getMetadata().getDatabaseType(); + String databaseType = this.metadata().getDatabaseType(); int type = 0; if (databaseType.contains("GeoIP2-Anonymous-IP")) { type |= DatabaseType.ANONYMOUS_IP.type; @@ -255,7 +255,7 @@ private LookupResult get(InetAddress ipAddress, Class cls, String caller = Thread.currentThread().getStackTrace()[3] .getMethodName(); throw new UnsupportedOperationException( - "Invalid attempt to open a " + getMetadata().getDatabaseType() + "Invalid attempt to open a " + metadata().getDatabaseType() + " database using the " + caller + " method"); } @@ -735,7 +735,16 @@ private Optional getIsp( /** * @return the metadata for the open MaxMind DB file. */ - public Metadata getMetadata() { + public Metadata metadata() { return this.reader.getMetadata(); } + + /** + * @return the metadata for the open MaxMind DB file. + * @deprecated Use {@link #metadata()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public Metadata getMetadata() { + return metadata(); + } } diff --git a/src/main/java/com/maxmind/geoip2/exception/HttpException.java b/src/main/java/com/maxmind/geoip2/exception/HttpException.java index a73d8f9dc..7cf497b66 100644 --- a/src/main/java/com/maxmind/geoip2/exception/HttpException.java +++ b/src/main/java/com/maxmind/geoip2/exception/HttpException.java @@ -39,16 +39,34 @@ public HttpException(String message, int httpStatus, URI uri, /** * @return the HTTP status of the query that caused the exception. */ - public int getHttpStatus() { + public int httpStatus() { return this.httpStatus; } + /** + * @return the HTTP status of the query that caused the exception. + * @deprecated Use {@link #httpStatus()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public int getHttpStatus() { + return httpStatus(); + } + /** * @return the URI queried. */ - public URI getUri() { + public URI uri() { return this.uri; } + /** + * @return the URI queried. + * @deprecated Use {@link #uri()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public URI getUri() { + return uri(); + } + } diff --git a/src/main/java/com/maxmind/geoip2/exception/InvalidRequestException.java b/src/main/java/com/maxmind/geoip2/exception/InvalidRequestException.java index 58c845d7f..1acd388e5 100644 --- a/src/main/java/com/maxmind/geoip2/exception/InvalidRequestException.java +++ b/src/main/java/com/maxmind/geoip2/exception/InvalidRequestException.java @@ -39,15 +39,33 @@ public InvalidRequestException(String message, String code, int httpStatus, /** * @return The error code returned by the MaxMind web service. */ - public String getCode() { + public String code() { return this.code; } + /** + * @return The error code returned by the MaxMind web service. + * @deprecated Use {@link #code()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public String getCode() { + return code(); + } + /** * @return the URI queried. */ - public URI getUri() { + public URI uri() { return this.uri; } + /** + * @return the URI queried. + * @deprecated Use {@link #uri()} instead. This method will be removed in 6.0.0. + */ + @Deprecated(since = "5.0.0", forRemoval = true) + public URI getUri() { + return uri(); + } + } From 2192c59e94c237197d14619f9de90a7716aac5f4 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 14 Oct 2025 13:21:31 -0700 Subject: [PATCH 03/17] Update to maxmind-db 4.0.0 and use non-deprecated accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump maxmind-db dependency from 3.2.0 to 4.0.0-SNAPSHOT - Update method calls to use record-style accessors instead of deprecated JavaBean-style getters in DatabaseReader.java and test files - Changes include: getDatabaseType() → databaseType(), getData() → data(), getNetwork() → network(), getNetworkAddress() → networkAddress(), getPrefixLength() → prefixLength() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pom.xml | 2 +- src/main/java/com/maxmind/geoip2/DatabaseReader.java | 8 ++++---- src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java | 2 +- .../java/com/maxmind/geoip2/NetworkDeserializerTest.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 585ee5aa0..1ad623533 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ com.maxmind.db maxmind-db - 3.2.0 + 4.0.0-SNAPSHOT com.fasterxml.jackson.core diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index 6a88283d8..20fe93c4e 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -115,7 +115,7 @@ private DatabaseReader(Builder builder) throws IOException { } private int getDatabaseType() { - String databaseType = this.metadata().getDatabaseType(); + String databaseType = this.metadata().databaseType(); int type = 0; if (databaseType.contains("GeoIP2-Anonymous-IP")) { type |= DatabaseType.ANONYMOUS_IP.type; @@ -255,15 +255,15 @@ private LookupResult get(InetAddress ipAddress, Class cls, String caller = Thread.currentThread().getStackTrace()[3] .getMethodName(); throw new UnsupportedOperationException( - "Invalid attempt to open a " + metadata().getDatabaseType() + "Invalid attempt to open a " + metadata().databaseType() + " database using the " + caller + " method"); } DatabaseRecord record = reader.getRecord(ipAddress, cls); - T o = record.getData(); + T o = record.data(); - return new LookupResult<>(o, ipAddress.getHostAddress(), record.getNetwork()); + return new LookupResult<>(o, ipAddress.getHostAddress(), record.network()); } /** diff --git a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java index a3e22e2df..060219e4c 100644 --- a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java +++ b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java @@ -132,7 +132,7 @@ private void testMemoryMode(DatabaseReader reader) throws IOException, public void metadata() throws IOException { DatabaseReader reader = new DatabaseReader.Builder(this.geoipFile) .fileMode(Reader.FileMode.MEMORY).build(); - assertEquals("GeoIP2-City", reader.metadata().getDatabaseType()); + assertEquals("GeoIP2-City", reader.metadata().databaseType()); } @Test diff --git a/src/test/java/com/maxmind/geoip2/NetworkDeserializerTest.java b/src/test/java/com/maxmind/geoip2/NetworkDeserializerTest.java index 60acf26d0..35b2d57ca 100644 --- a/src/test/java/com/maxmind/geoip2/NetworkDeserializerTest.java +++ b/src/test/java/com/maxmind/geoip2/NetworkDeserializerTest.java @@ -23,8 +23,8 @@ private static Network parse(String jsonString) throws IOException { } private static void assertNetwork(Network n, String addr, int prefix) throws Exception { assertNotNull(n); - assertEquals(InetAddress.getByName(addr), n.getNetworkAddress()); - assertEquals(prefix, n.getPrefixLength()); + assertEquals(InetAddress.getByName(addr), n.networkAddress()); + assertEquals(prefix, n.prefixLength()); } @Test From fe27a08791f036d909d0359e1bdc4e7d845d19ba Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Tue, 14 Oct 2025 13:47:14 -0700 Subject: [PATCH 04/17] Remove most uses of @MaxMindDbConstructor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With maxmind-db 4.0.0, records automatically use their canonical constructor for deserialization when no constructor is explicitly annotated with @MaxMindDbConstructor. This change removes the annotation from most records in the codebase. Changes: - Removed empty canonical constructors from records with no logic - Removed @MaxMindDbConstructor from canonical constructors with immutability/validation logic, relying on automatic detection - Added useDefault=true to boolean parameters to leverage automatic null-to-false conversion instead of manual Boolean→boolean handling - Kept @MaxMindDbConstructor only for records requiring custom type conversions (String→LocalDate in AnonymousPlusResponse, String→enum in ConnectionTypeResponse and Traits) - Removed unused MaxMindDbConstructor imports All tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../geoip2/model/AnonymousIpResponse.java | 48 ++----------- .../geoip2/model/AnonymousPlusResponse.java | 50 +++++++------ .../com/maxmind/geoip2/model/AsnResponse.java | 8 --- .../maxmind/geoip2/model/CityResponse.java | 2 - .../maxmind/geoip2/model/CountryResponse.java | 2 - .../maxmind/geoip2/model/DomainResponse.java | 8 --- .../geoip2/model/EnterpriseResponse.java | 2 - .../maxmind/geoip2/model/IpRiskResponse.java | 51 ++------------ .../com/maxmind/geoip2/model/IspResponse.java | 8 --- .../java/com/maxmind/geoip2/record/City.java | 2 - .../com/maxmind/geoip2/record/Continent.java | 2 - .../com/maxmind/geoip2/record/Country.java | 37 +--------- .../com/maxmind/geoip2/record/Location.java | 8 --- .../com/maxmind/geoip2/record/MaxMind.java | 8 --- .../com/maxmind/geoip2/record/Postal.java | 8 --- .../geoip2/record/RepresentedCountry.java | 41 +---------- .../maxmind/geoip2/record/Subdivision.java | 2 - .../com/maxmind/geoip2/record/Traits.java | 70 ++++++++++--------- 18 files changed, 78 insertions(+), 279 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java index 573e04765..922a484cd 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -34,27 +33,27 @@ public record AnonymousIpResponse( String ipAddress, @JsonProperty("is_anonymous") - @MaxMindDbParameter(name = "is_anonymous") + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @JsonProperty("is_anonymous_vpn") - @MaxMindDbParameter(name = "is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) boolean isAnonymousVpn, @JsonProperty("is_hosting_provider") - @MaxMindDbParameter(name = "is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) boolean isHostingProvider, @JsonProperty("is_public_proxy") - @MaxMindDbParameter(name = "is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) boolean isPublicProxy, @JsonProperty("is_residential_proxy") - @MaxMindDbParameter(name = "is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) boolean isResidentialProxy, @JsonProperty("is_tor_exit_node") - @MaxMindDbParameter(name = "is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) boolean isTorExitNode, @JsonProperty("network") @@ -63,41 +62,6 @@ public record AnonymousIpResponse( Network network ) implements JsonSerializable { - /** - * Constructs an instance of {@code AnonymousIpResponse} with nullable boolean fields. - * - * @param ipAddress the IP address being checked - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - */ - @MaxMindDbConstructor - public AnonymousIpResponse( - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "is_anonymous") Boolean isAnonymous, - @MaxMindDbParameter(name = "is_anonymous_vpn") Boolean isAnonymousVpn, - @MaxMindDbParameter(name = "is_hosting_provider") Boolean isHostingProvider, - @MaxMindDbParameter(name = "is_public_proxy") Boolean isPublicProxy, - @MaxMindDbParameter(name = "is_residential_proxy") Boolean isResidentialProxy, - @MaxMindDbParameter(name = "is_tor_exit_node") Boolean isTorExitNode, - @MaxMindDbParameter(name = "network") Network network - ) { - this( - ipAddress, - isAnonymous != null ? isAnonymous : false, - isAnonymousVpn != null ? isAnonymousVpn : false, - isHostingProvider != null ? isHostingProvider : false, - isPublicProxy != null ? isPublicProxy : false, - isResidentialProxy != null ? isResidentialProxy : false, - isTorExitNode != null ? isTorExitNode : false, - network - ); - } - /** * Constructs an instance of {@code AnonymousIpResponse} from the values in the passed * response and the specified IP address and network. diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java index 2a7a66162..15a1c8221 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java @@ -41,27 +41,27 @@ public record AnonymousPlusResponse( String ipAddress, @JsonProperty("is_anonymous") - @MaxMindDbParameter(name = "is_anonymous") + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @JsonProperty("is_anonymous_vpn") - @MaxMindDbParameter(name = "is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) boolean isAnonymousVpn, @JsonProperty("is_hosting_provider") - @MaxMindDbParameter(name = "is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) boolean isHostingProvider, @JsonProperty("is_public_proxy") - @MaxMindDbParameter(name = "is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) boolean isPublicProxy, @JsonProperty("is_residential_proxy") - @MaxMindDbParameter(name = "is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) boolean isResidentialProxy, @JsonProperty("is_tor_exit_node") - @MaxMindDbParameter(name = "is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) boolean isTorExitNode, @JsonProperty("network") @@ -83,10 +83,9 @@ public record AnonymousPlusResponse( ) implements JsonSerializable { /** - * Constructs an instance of {@code AnonymousPlusResponse} with nullable boolean fields - * and date parsing from MaxMind database. + * Constructs an instance of {@code AnonymousPlusResponse} with date parsing + * from MaxMind database. * - * @param anonymizerConfidence confidence that the network is a VPN. * @param ipAddress the IP address being checked * @param isAnonymous whether the IP address belongs to any sort of anonymous network * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system @@ -95,31 +94,38 @@ public record AnonymousPlusResponse( * @param isResidentialProxy whether the IP address belongs to a residential proxy system * @param isTorExitNode whether the IP address is a Tor exit node * @param network the network associated with the record + * @param anonymizerConfidence confidence that the network is a VPN. * @param networkLastSeen the last sighting of the network. * @param providerName the name of the VPN provider. */ @MaxMindDbConstructor public AnonymousPlusResponse( - @MaxMindDbParameter(name = "anonymizer_confidence") Integer anonymizerConfidence, @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "is_anonymous") Boolean isAnonymous, - @MaxMindDbParameter(name = "is_anonymous_vpn") Boolean isAnonymousVpn, - @MaxMindDbParameter(name = "is_hosting_provider") Boolean isHostingProvider, - @MaxMindDbParameter(name = "is_public_proxy") Boolean isPublicProxy, - @MaxMindDbParameter(name = "is_residential_proxy") Boolean isResidentialProxy, - @MaxMindDbParameter(name = "is_tor_exit_node") Boolean isTorExitNode, + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) + boolean isAnonymous, + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) + boolean isAnonymousVpn, + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) + boolean isHostingProvider, + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) + boolean isPublicProxy, + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) + boolean isResidentialProxy, + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) + boolean isTorExitNode, @MaxMindDbParameter(name = "network") Network network, + @MaxMindDbParameter(name = "anonymizer_confidence") Integer anonymizerConfidence, @MaxMindDbParameter(name = "network_last_seen") String networkLastSeen, @MaxMindDbParameter(name = "provider_name") String providerName ) { this( ipAddress, - isAnonymous != null ? isAnonymous : false, - isAnonymousVpn != null ? isAnonymousVpn : false, - isHostingProvider != null ? isHostingProvider : false, - isPublicProxy != null ? isPublicProxy : false, - isResidentialProxy != null ? isResidentialProxy : false, - isTorExitNode != null ? isTorExitNode : false, + isAnonymous, + isAnonymousVpn, + isHostingProvider, + isPublicProxy, + isResidentialProxy, + isTorExitNode, network, anonymizerConfidence, networkLastSeen != null ? LocalDate.parse(networkLastSeen) : null, diff --git a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java index 56d22abcc..8bbbe1da2 100644 --- a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -40,13 +39,6 @@ public record AsnResponse( Network network ) implements JsonSerializable { - /** - * Canonical constructor. - */ - @MaxMindDbConstructor - public AsnResponse { - } - /** * Constructs an instance of {@code AsnResponse} with only the specified values set. * diff --git a/src/main/java/com/maxmind/geoip2/model/CityResponse.java b/src/main/java/com/maxmind/geoip2/model/CityResponse.java index bbc83552b..670ad7bc2 100644 --- a/src/main/java/com/maxmind/geoip2/model/CityResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CityResponse.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -91,7 +90,6 @@ public record CityResponse( /** * Compact canonical constructor that sets defaults for null values. */ - @MaxMindDbConstructor public CityResponse { city = city != null ? city : new City(); continent = continent != null ? continent : new Continent(); diff --git a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java index eb19ec901..7d449cab0 100644 --- a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -60,7 +59,6 @@ public record CountryResponse( /** * Compact canonical constructor that sets defaults for null values. */ - @MaxMindDbConstructor public CountryResponse { continent = continent != null ? continent : new Continent(); country = country != null ? country : new Country(); diff --git a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java index 97b994994..542a607bf 100644 --- a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -35,13 +34,6 @@ public record DomainResponse( Network network ) implements JsonSerializable { - /** - * Canonical constructor. - */ - @MaxMindDbConstructor - public DomainResponse { - } - /** * Constructs an instance of {@code DomainResponse} with only required parameters. * diff --git a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java index 4322e7fe8..5d0db1376 100644 --- a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -91,7 +90,6 @@ public record EnterpriseResponse( /** * Compact canonical constructor that sets defaults for null values. */ - @MaxMindDbConstructor public EnterpriseResponse { city = city != null ? city : new City(); continent = continent != null ? continent : new Continent(); diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index f801ca930..c0582e31c 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -35,27 +34,27 @@ public record IpRiskResponse( String ipAddress, @JsonProperty("is_anonymous") - @MaxMindDbParameter(name = "is_anonymous") + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @JsonProperty("is_anonymous_vpn") - @MaxMindDbParameter(name = "is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) boolean isAnonymousVpn, @JsonProperty("is_hosting_provider") - @MaxMindDbParameter(name = "is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) boolean isHostingProvider, @JsonProperty("is_public_proxy") - @MaxMindDbParameter(name = "is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) boolean isPublicProxy, @JsonProperty("is_residential_proxy") - @MaxMindDbParameter(name = "is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) boolean isResidentialProxy, @JsonProperty("is_tor_exit_node") - @MaxMindDbParameter(name = "is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) boolean isTorExitNode, @JsonProperty("network") @@ -68,44 +67,6 @@ public record IpRiskResponse( double ipRisk ) implements JsonSerializable { - /** - * Constructs an instance of {@code IpRiskResponse} with nullable boolean fields. - * - * @param ipAddress the IP address being checked - * @param isAnonymous whether the IP address belongs to any sort of anonymous network - * @param isAnonymousVpn whether the IP address belongs to an anonymous VPN system - * @param isHostingProvider whether the IP address belongs to a hosting provider - * @param isPublicProxy whether the IP address belongs to a public proxy system - * @param isResidentialProxy whether the IP address belongs to a residential proxy system - * @param isTorExitNode whether the IP address is a Tor exit node - * @param network the network associated with the record - * @param ipRisk the IP risk of a model - */ - @MaxMindDbConstructor - public IpRiskResponse( - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "is_anonymous") Boolean isAnonymous, - @MaxMindDbParameter(name = "is_anonymous_vpn") Boolean isAnonymousVpn, - @MaxMindDbParameter(name = "is_hosting_provider") Boolean isHostingProvider, - @MaxMindDbParameter(name = "is_public_proxy") Boolean isPublicProxy, - @MaxMindDbParameter(name = "is_residential_proxy") Boolean isResidentialProxy, - @MaxMindDbParameter(name = "is_tor_exit_node") Boolean isTorExitNode, - @MaxMindDbParameter(name = "network") Network network, - @MaxMindDbParameter(name = "ip_risk") double ipRisk - ) { - this( - ipAddress, - isAnonymous != null ? isAnonymous : false, - isAnonymousVpn != null ? isAnonymousVpn : false, - isHostingProvider != null ? isHostingProvider : false, - isPublicProxy != null ? isPublicProxy : false, - isResidentialProxy != null ? isResidentialProxy : false, - isTorExitNode != null ? isTorExitNode : false, - network, - ipRisk - ); - } - /** * Constructs an instance of {@code IpRiskResponse}. * diff --git a/src/main/java/com/maxmind/geoip2/model/IspResponse.java b/src/main/java/com/maxmind/geoip2/model/IspResponse.java index 83e21d014..eca1a7618 100644 --- a/src/main/java/com/maxmind/geoip2/model/IspResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IspResponse.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -66,13 +65,6 @@ public record IspResponse( Network network ) implements JsonSerializable { - /** - * Canonical constructor. - */ - @MaxMindDbConstructor - public IspResponse { - } - /** * Constructs an instance of {@code IspResponse}. * diff --git a/src/main/java/com/maxmind/geoip2/record/City.java b/src/main/java/com/maxmind/geoip2/record/City.java index 223870e9b..e8902dcb2 100644 --- a/src/main/java/com/maxmind/geoip2/record/City.java +++ b/src/main/java/com/maxmind/geoip2/record/City.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; import java.util.ArrayList; @@ -47,7 +46,6 @@ public record City( /** * Compact canonical constructor that ensures immutability and handles null values. */ - @MaxMindDbConstructor public City { locales = locales != null ? List.copyOf(locales) : List.of(); names = names != null ? Map.copyOf(names) : Map.of(); diff --git a/src/main/java/com/maxmind/geoip2/record/Continent.java b/src/main/java/com/maxmind/geoip2/record/Continent.java index 435ab586a..968e100ab 100644 --- a/src/main/java/com/maxmind/geoip2/record/Continent.java +++ b/src/main/java/com/maxmind/geoip2/record/Continent.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; import java.util.List; @@ -44,7 +43,6 @@ public record Continent( /** * Compact canonical constructor that ensures immutability and handles null values. */ - @MaxMindDbConstructor public Continent { locales = locales != null ? List.copyOf(locales) : List.of(); names = names != null ? Map.copyOf(names) : Map.of(); diff --git a/src/main/java/com/maxmind/geoip2/record/Country.java b/src/main/java/com/maxmind/geoip2/record/Country.java index 9425d91c3..17e4e699b 100644 --- a/src/main/java/com/maxmind/geoip2/record/Country.java +++ b/src/main/java/com/maxmind/geoip2/record/Country.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; import java.util.List; @@ -42,7 +41,7 @@ public record Country( Long geonameId, @JsonProperty("is_in_european_union") - @MaxMindDbParameter(name = "is_in_european_union") + @MaxMindDbParameter(name = "is_in_european_union", useDefault = true) boolean isInEuropeanUnion, @JsonProperty("iso_code") @@ -62,40 +61,6 @@ public record Country( names = names != null ? Map.copyOf(names) : Map.of(); } - /** - * Constructs an instance of {@code Country}. - * - * @param locales The locales to use. - * @param confidence This is a value from 0-100 indicating MaxMind's - * confidence that the country is correct. - * @param geonameId This is a GeoName ID for the country. - * @param isInEuropeanUnion This is true if the country is a member state of - * the European Union. - * @param isoCode This is a string up to three characters long contain the - * country code. - * @param names This is a map from locale codes to the names for the country - * in that locale. - */ - @MaxMindDbConstructor - public Country( - @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, - @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geonameId, - @JsonProperty("is_in_european_union") @MaxMindDbParameter(name = "is_in_european_union") - Boolean isInEuropeanUnion, - @JsonProperty("iso_code") @MaxMindDbParameter(name = "iso_code") String isoCode, - @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names - ) { - this( - locales, - confidence, - geonameId, - isInEuropeanUnion != null ? isInEuropeanUnion : false, - isoCode, - names - ); - } - /** * Constructs an instance of {@code Country} with no data. */ diff --git a/src/main/java/com/maxmind/geoip2/record/Location.java b/src/main/java/com/maxmind/geoip2/record/Location.java index 6a1fbf918..846d36251 100644 --- a/src/main/java/com/maxmind/geoip2/record/Location.java +++ b/src/main/java/com/maxmind/geoip2/record/Location.java @@ -1,7 +1,6 @@ package com.maxmind.geoip2.record; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.JsonSerializable; @@ -57,13 +56,6 @@ public record Location( String timeZone ) implements JsonSerializable { - /** - * Compact canonical constructor. - */ - @MaxMindDbConstructor - public Location { - } - /** * Constructs a {@code Location} record with {@code null} values for all the fields. */ diff --git a/src/main/java/com/maxmind/geoip2/record/MaxMind.java b/src/main/java/com/maxmind/geoip2/record/MaxMind.java index 8d6caea3a..18a0307a0 100644 --- a/src/main/java/com/maxmind/geoip2/record/MaxMind.java +++ b/src/main/java/com/maxmind/geoip2/record/MaxMind.java @@ -1,7 +1,6 @@ package com.maxmind.geoip2.record; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.JsonSerializable; @@ -19,13 +18,6 @@ public record MaxMind( Integer queriesRemaining ) implements JsonSerializable { - /** - * Compact canonical constructor. - */ - @MaxMindDbConstructor - public MaxMind { - } - /** * Constructs a {@code MaxMind} record. */ diff --git a/src/main/java/com/maxmind/geoip2/record/Postal.java b/src/main/java/com/maxmind/geoip2/record/Postal.java index 21393164c..be7680a5e 100644 --- a/src/main/java/com/maxmind/geoip2/record/Postal.java +++ b/src/main/java/com/maxmind/geoip2/record/Postal.java @@ -1,7 +1,6 @@ package com.maxmind.geoip2.record; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.JsonSerializable; @@ -27,13 +26,6 @@ public record Postal( Integer confidence ) implements JsonSerializable { - /** - * Compact canonical constructor. - */ - @MaxMindDbConstructor - public Postal { - } - /** * Constructs a {@code Postal} record. */ diff --git a/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java b/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java index 40e779d84..be9c02341 100644 --- a/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java +++ b/src/main/java/com/maxmind/geoip2/record/RepresentedCountry.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; import java.util.List; @@ -50,7 +49,7 @@ public record RepresentedCountry( Long geonameId, @JsonProperty("is_in_european_union") - @MaxMindDbParameter(name = "is_in_european_union") + @MaxMindDbParameter(name = "is_in_european_union", useDefault = true) boolean isInEuropeanUnion, @JsonProperty("iso_code") @@ -74,44 +73,6 @@ public record RepresentedCountry( names = names != null ? Map.copyOf(names) : Map.of(); } - /** - * Constructs an instance of {@code RepresentedCountry}. - * - * @param locales The locales to use. - * @param confidence This is a value from 0-100 indicating MaxMind's - * confidence that the country is correct. - * @param geonameId This is a GeoName ID for the country. - * @param isInEuropeanUnion This is true if the country is a member state of - * the European Union. - * @param isoCode This is a string up to three characters long contain the - * country code. - * @param names This is a map from locale codes to the names for the country - * in that locale. - * @param type This is a string indicating the type of entity that is - * representing the country. - */ - @MaxMindDbConstructor - public RepresentedCountry( - @JacksonInject("locales") @MaxMindDbParameter(name = "locales") List locales, - @JsonProperty("confidence") @MaxMindDbParameter(name = "confidence") Integer confidence, - @JsonProperty("geoname_id") @MaxMindDbParameter(name = "geoname_id") Long geonameId, - @JsonProperty("is_in_european_union") @MaxMindDbParameter(name = "is_in_european_union") - Boolean isInEuropeanUnion, - @JsonProperty("iso_code") @MaxMindDbParameter(name = "iso_code") String isoCode, - @JsonProperty("names") @MaxMindDbParameter(name = "names") Map names, - @JsonProperty("type") @MaxMindDbParameter(name = "type") String type - ) { - this( - locales, - confidence, - geonameId, - isInEuropeanUnion != null ? isInEuropeanUnion : false, - isoCode, - names, - type - ); - } - /** * Constructs an instance of {@code RepresentedCountry} with no data. */ diff --git a/src/main/java/com/maxmind/geoip2/record/Subdivision.java b/src/main/java/com/maxmind/geoip2/record/Subdivision.java index 80c11f537..31c5c3fac 100644 --- a/src/main/java/com/maxmind/geoip2/record/Subdivision.java +++ b/src/main/java/com/maxmind/geoip2/record/Subdivision.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; -import com.maxmind.db.MaxMindDbConstructor; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; import java.util.List; @@ -52,7 +51,6 @@ public record Subdivision( /** * Compact canonical constructor that ensures immutability and handles null values. */ - @MaxMindDbConstructor public Subdivision { locales = locales != null ? List.copyOf(locales) : List.of(); names = names != null ? Map.copyOf(names) : Map.of(); diff --git a/src/main/java/com/maxmind/geoip2/record/Traits.java b/src/main/java/com/maxmind/geoip2/record/Traits.java index 6bfba9aab..825e03cc8 100644 --- a/src/main/java/com/maxmind/geoip2/record/Traits.java +++ b/src/main/java/com/maxmind/geoip2/record/Traits.java @@ -93,35 +93,35 @@ public record Traits( String ipAddress, @JsonProperty("is_anonymous") - @MaxMindDbParameter(name = "is_anonymous") + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @JsonProperty("is_anonymous_vpn") - @MaxMindDbParameter(name = "is_anonymous_vpn") + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) boolean isAnonymousVpn, @JsonProperty("is_anycast") - @MaxMindDbParameter(name = "is_anycast") + @MaxMindDbParameter(name = "is_anycast", useDefault = true) boolean isAnycast, @JsonProperty("is_hosting_provider") - @MaxMindDbParameter(name = "is_hosting_provider") + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) boolean isHostingProvider, @JsonProperty("is_legitimate_proxy") - @MaxMindDbParameter(name = "is_legitimate_proxy") + @MaxMindDbParameter(name = "is_legitimate_proxy", useDefault = true) boolean isLegitimateProxy, @JsonProperty("is_public_proxy") - @MaxMindDbParameter(name = "is_public_proxy") + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) boolean isPublicProxy, @JsonProperty("is_residential_proxy") - @MaxMindDbParameter(name = "is_residential_proxy") + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) boolean isResidentialProxy, @JsonProperty("is_tor_exit_node") - @MaxMindDbParameter(name = "is_tor_exit_node") + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) boolean isTorExitNode, @JsonProperty("isp") @@ -159,17 +159,11 @@ public record Traits( Double staticIpScore ) implements JsonSerializable { - /** - * Compact canonical constructor. - */ - public Traits { - } - /** * Constructs an instance of {@code Traits}. */ public Traits() { - this(null, null, null, null, + this(null, null, (ConnectionType) null, null, null, false, false, false, false, false, false, false, false, null, null, null, null, null, null, null, null); @@ -182,18 +176,18 @@ public Traits() { * @param network the network */ public Traits(String ipAddress, Network network) { - this(null, null, null, null, + this(null, null, (ConnectionType) null, null, ipAddress, false, false, false, false, false, false, false, false, null, null, null, network, null, null, null, null); } /** - * Constructs an instance of {@code Traits}. + * Constructs an instance of {@code Traits} from MaxMind database with type conversions. * * @param autonomousSystemNumber the autonomous system number * @param autonomousSystemOrganization the autonomous system organization - * @param connectionType the connection type + * @param connectionType the connection type as a string * @param domain the domain * @param ipAddress the IP address * @param isAnonymous the anonymous flag @@ -221,14 +215,22 @@ public Traits( @MaxMindDbParameter(name = "connection_type") String connectionType, @MaxMindDbParameter(name = "domain") String domain, @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "is_anonymous") Boolean isAnonymous, - @MaxMindDbParameter(name = "is_anonymous_vpn") Boolean isAnonymousVpn, - @MaxMindDbParameter(name = "is_anycast") Boolean isAnycast, - @MaxMindDbParameter(name = "is_hosting_provider") Boolean isHostingProvider, - @MaxMindDbParameter(name = "is_legitimate_proxy") Boolean isLegitimateProxy, - @MaxMindDbParameter(name = "is_public_proxy") Boolean isPublicProxy, - @MaxMindDbParameter(name = "is_residential_proxy") Boolean isResidentialProxy, - @MaxMindDbParameter(name = "is_tor_exit_node") Boolean isTorExitNode, + @MaxMindDbParameter(name = "is_anonymous", useDefault = true) + boolean isAnonymous, + @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) + boolean isAnonymousVpn, + @MaxMindDbParameter(name = "is_anycast", useDefault = true) + boolean isAnycast, + @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) + boolean isHostingProvider, + @MaxMindDbParameter(name = "is_legitimate_proxy", useDefault = true) + boolean isLegitimateProxy, + @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) + boolean isPublicProxy, + @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) + boolean isResidentialProxy, + @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) + boolean isTorExitNode, @MaxMindDbParameter(name = "isp") String isp, @MaxMindDbParameter(name = "mobile_country_code") String mobileCountryCode, @MaxMindDbParameter(name = "mobile_network_code") String mobileNetworkCode, @@ -244,14 +246,14 @@ public Traits( ConnectionType.fromString(connectionType), domain, ipAddress, - isAnonymous != null ? isAnonymous : false, - isAnonymousVpn != null ? isAnonymousVpn : false, - isAnycast != null ? isAnycast : false, - isHostingProvider != null ? isHostingProvider : false, - isLegitimateProxy != null ? isLegitimateProxy : false, - isPublicProxy != null ? isPublicProxy : false, - isResidentialProxy != null ? isResidentialProxy : false, - isTorExitNode != null ? isTorExitNode : false, + isAnonymous, + isAnonymousVpn, + isAnycast, + isHostingProvider, + isLegitimateProxy, + isPublicProxy, + isResidentialProxy, + isTorExitNode, isp, mobileCountryCode, mobileNetworkCode, From 89dad0ffe62ee69dba417c75ece6084401facc1e Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 10:11:06 -0700 Subject: [PATCH 05/17] Remove redundant constructors now that IP/network are auto-injected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove copy constructors from model classes that only populated IP address and network (AnonymousIpResponse, AnonymousPlusResponse, ConnectionTypeResponse, AsnResponse, DomainResponse, IspResponse, IpRiskResponse) - Update DatabaseReader getter methods to return responses directly - Update annotations to use @MaxMindDbIpAddress and @MaxMindDbNetwork for context injection - Remove @MaxMindDbConstructor from Traits that was only needed for ConnectionType conversion - Add @MaxMindDbCreator to ConnectionType.fromString() for enum deserialization These constructors are no longer needed since IP address and network are now automatically injected via @MaxMindDbIpAddress and @MaxMindDbNetwork annotations, and enum conversion is handled by @MaxMindDbCreator. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../com/maxmind/geoip2/DatabaseReader.java | 72 ++------- .../geoip2/model/AnonymousIpResponse.java | 31 +--- .../geoip2/model/AnonymousPlusResponse.java | 38 +---- .../com/maxmind/geoip2/model/AsnResponse.java | 26 +--- .../maxmind/geoip2/model/CityResponse.java | 6 +- .../geoip2/model/ConnectionTypeResponse.java | 49 +----- .../maxmind/geoip2/model/CountryResponse.java | 6 +- .../maxmind/geoip2/model/DomainResponse.java | 21 +-- .../geoip2/model/EnterpriseResponse.java | 6 +- .../maxmind/geoip2/model/IpRiskResponse.java | 31 +--- .../com/maxmind/geoip2/model/IspResponse.java | 30 +--- .../com/maxmind/geoip2/record/Traits.java | 140 +----------------- 12 files changed, 48 insertions(+), 408 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index 20fe93c4e..bf2bc7dd0 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -317,8 +317,6 @@ private Optional getCountry( return Optional.of( new CountryResponse( response, - result.ipAddress(), - result.network(), locales ) ); @@ -354,12 +352,7 @@ private Optional getCity( return Optional.empty(); } return Optional.of( - new CityResponse( - response, - result.ipAddress(), - result.network(), - locales - ) + new CityResponse(response, locales) ); } @@ -400,13 +393,7 @@ private Optional getAnonymousIp( if (response == null) { return Optional.empty(); } - return Optional.of( - new AnonymousIpResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** @@ -447,13 +434,7 @@ private Optional getAnonymousPlus( if (response == null) { return Optional.empty(); } - return Optional.of( - new AnonymousPlusResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } @@ -493,13 +474,7 @@ private Optional getIpRisk(InetAddress ipAddress) throws IOExcep if (response == null) { return Optional.empty(); } - return Optional.of( - new IpRiskResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** @@ -538,13 +513,7 @@ private Optional getAsn(InetAddress ipAddress) if (response == null) { return Optional.empty(); } - return Optional.of( - new AsnResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** @@ -584,13 +553,7 @@ private Optional getConnectionType( if (response == null) { return Optional.empty(); } - return Optional.of( - new ConnectionTypeResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** @@ -630,13 +593,7 @@ private Optional getDomain( if (response == null) { return Optional.empty(); } - return Optional.of( - new DomainResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** @@ -677,12 +634,7 @@ private Optional getEnterprise( return Optional.empty(); } return Optional.of( - new EnterpriseResponse( - response, - result.ipAddress(), - result.network(), - locales - ) + new EnterpriseResponse(response, locales) ); } @@ -723,13 +675,7 @@ private Optional getIsp( if (response == null) { return Optional.empty(); } - return Optional.of( - new IspResponse( - response, - result.ipAddress(), - result.network() - ) - ); + return Optional.of(response); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java index 922a484cd..eb6104018 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -29,7 +31,7 @@ */ public record AnonymousIpResponse( @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("is_anonymous") @@ -58,35 +60,10 @@ public record AnonymousIpResponse( @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network ) implements JsonSerializable { - /** - * Constructs an instance of {@code AnonymousIpResponse} from the values in the passed - * response and the specified IP address and network. - * - * @param response the response to copy - * @param ipAddress the IP address being checked - * @param network the network associated with the record - */ - public AnonymousIpResponse( - AnonymousIpResponse response, - String ipAddress, - Network network - ) { - this( - ipAddress, - response.isAnonymous(), - response.isAnonymousVpn(), - response.isHostingProvider(), - response.isPublicProxy(), - response.isResidentialProxy(), - response.isTorExitNode(), - network - ); - } - /** * @return The IP address that the data in the model is for. * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java index 15a1c8221..4a637eee3 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.maxmind.db.MaxMindDbConstructor; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -37,7 +39,7 @@ */ public record AnonymousPlusResponse( @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("is_anonymous") @@ -66,7 +68,7 @@ public record AnonymousPlusResponse( @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network, @JsonProperty("anonymizer_confidence") @@ -100,7 +102,7 @@ public record AnonymousPlusResponse( */ @MaxMindDbConstructor public AnonymousPlusResponse( - @MaxMindDbParameter(name = "ip_address") String ipAddress, + @MaxMindDbIpAddress String ipAddress, @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) @@ -113,7 +115,7 @@ public AnonymousPlusResponse( boolean isResidentialProxy, @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) boolean isTorExitNode, - @MaxMindDbParameter(name = "network") Network network, + @MaxMindDbNetwork Network network, @MaxMindDbParameter(name = "anonymizer_confidence") Integer anonymizerConfidence, @MaxMindDbParameter(name = "network_last_seen") String networkLastSeen, @MaxMindDbParameter(name = "provider_name") String providerName @@ -133,34 +135,6 @@ public AnonymousPlusResponse( ); } - /** - * Constructs an instance of {@code AnonymousPlusResponse} from the values in the - * response and the specified IP address and network. - * - * @param response the response to copy - * @param ipAddress the IP address being checked - * @param network the network associated with the record - */ - public AnonymousPlusResponse( - AnonymousPlusResponse response, - String ipAddress, - Network network - ) { - this( - ipAddress, - response.isAnonymous(), - response.isAnonymousVpn(), - response.isHostingProvider(), - response.isPublicProxy(), - response.isResidentialProxy(), - response.isTorExitNode(), - network, - response.anonymizerConfidence(), - response.networkLastSeen(), - response.providerName() - ); - } - /** * @return The IP address that the data in the model is for. * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. diff --git a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java index 8bbbe1da2..c2bcea216 100644 --- a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -30,35 +32,15 @@ public record AsnResponse( String autonomousSystemOrganization, @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network ) implements JsonSerializable { - /** - * Constructs an instance of {@code AsnResponse} with only the specified values set. - * - * @param response The {@code AsnResponse} object to copy. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ - public AsnResponse( - AsnResponse response, - String ipAddress, - Network network - ) { - this( - response.autonomousSystemNumber(), - response.autonomousSystemOrganization(), - ipAddress, - network - ); - } - /** * @return The autonomous system number associated with the IP address. * @deprecated Use {@link #autonomousSystemNumber()} instead. This method will be removed diff --git a/src/main/java/com/maxmind/geoip2/model/CityResponse.java b/src/main/java/com/maxmind/geoip2/model/CityResponse.java index 670ad7bc2..0ba099198 100644 --- a/src/main/java/com/maxmind/geoip2/model/CityResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CityResponse.java @@ -108,14 +108,10 @@ public record CityResponse( * Constructs an instance of {@code CityResponse} with the specified parameters. * * @param response the response - * @param ipAddress the IP address that the data in the model is for. - * @param network the network associated with the record. * @param locales the locales */ public CityResponse( CityResponse response, - String ipAddress, - Network network, List locales ) { this( @@ -128,7 +124,7 @@ public CityResponse( new Country(response.registeredCountry(), locales), new RepresentedCountry(response.representedCountry(), locales), mapSubdivisions(response.subdivisions(), locales), - new Traits(response.traits(), ipAddress, network) + response.traits() ); } diff --git a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java index 44d02aa85..3c7482347 100644 --- a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java @@ -7,7 +7,9 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; +import com.maxmind.db.MaxMindDbCreator; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -27,12 +29,12 @@ public record ConnectionTypeResponse( ConnectionType connectionType, @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network ) implements JsonSerializable { @@ -69,6 +71,7 @@ public String toString() { * @param s The string to create the instance from. */ @JsonCreator + @MaxMindDbCreator public static ConnectionType fromString(String s) { if (s == null) { return null; @@ -85,46 +88,6 @@ public static ConnectionType fromString(String s) { } } - /** - * Constructs an instance of {@code ConnectionTypeResponse} from MaxMind database - * with String-to-enum conversion. - * - * @param connectionType The connection type of the IP address as a string. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ - @MaxMindDbConstructor - public ConnectionTypeResponse( - @MaxMindDbParameter(name = "connection_type") String connectionType, - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "network") Network network - ) { - this( - ConnectionType.fromString(connectionType), - ipAddress, - network - ); - } - - /** - * Constructs an instance of {@code ConnectionTypeResponse}. - * - * @param response The {@code ConnectionTypeResponse} object to copy. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ - public ConnectionTypeResponse( - ConnectionTypeResponse response, - String ipAddress, - Network network - ) { - this( - response.connectionType(), - ipAddress, - network - ); - } - /** * @return The connection type of the IP address. * @deprecated Use {@link #connectionType()} instead. This method will be removed in 6.0.0. diff --git a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java index 7d449cab0..45bf4186e 100644 --- a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java @@ -73,14 +73,10 @@ public record CountryResponse( * Constructs an instance of {@code CountryResponse} with the specified parameters. * * @param response the response - * @param ipAddress the IP address that the data in the model is for. - * @param network the network associated with the record. * @param locales the locales */ public CountryResponse( CountryResponse response, - String ipAddress, - Network network, List locales ) { this( @@ -89,7 +85,7 @@ public CountryResponse( response.maxmind(), new Country(response.registeredCountry(), locales), new RepresentedCountry(response.representedCountry(), locales), - new Traits(response.traits(), ipAddress, network) + response.traits() ); } diff --git a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java index 542a607bf..c43a9cbaa 100644 --- a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -25,30 +27,15 @@ public record DomainResponse( String domain, @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network ) implements JsonSerializable { - /** - * Constructs an instance of {@code DomainResponse} with only required parameters. - * - * @param response the response - * @param ipAddress the IP address that the data in the model is for. - * @param network the network associated with the record. - */ - public DomainResponse( - DomainResponse response, - String ipAddress, - Network network - ) { - this(response.domain(), ipAddress, network); - } - /** * @return The second level domain associated with the IP address. This * will be something like "example.com" or "example.co.uk", not diff --git a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java index 5d0db1376..8855b34a1 100644 --- a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java @@ -108,14 +108,10 @@ public record EnterpriseResponse( * Constructs an instance of {@code EnterpriseResponse} with only required parameters. * * @param response the response - * @param ipAddress the IP address that the data in the model is for. - * @param network the network associated with the record. * @param locales the locales */ public EnterpriseResponse( EnterpriseResponse response, - String ipAddress, - Network network, List locales ) { this( @@ -128,7 +124,7 @@ public EnterpriseResponse( new Country(response.registeredCountry(), locales), new RepresentedCountry(response.representedCountry(), locales), mapSubdivisions(response.subdivisions(), locales), - new Traits(response.traits(), ipAddress, network) + response.traits() ); } diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index c0582e31c..8c2679468 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -30,7 +32,7 @@ */ public record IpRiskResponse( @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("is_anonymous") @@ -58,7 +60,7 @@ public record IpRiskResponse( boolean isTorExitNode, @JsonProperty("network") - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork @JsonDeserialize(using = NetworkDeserializer.class) Network network, @@ -67,31 +69,6 @@ public record IpRiskResponse( double ipRisk ) implements JsonSerializable { - /** - * Constructs an instance of {@code IpRiskResponse}. - * - * @param response The {@code IpRiskResponse} object to copy. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ - public IpRiskResponse( - IpRiskResponse response, - String ipAddress, - Network network - ) { - this( - ipAddress, - response.isAnonymous(), - response.isAnonymousVpn(), - response.isHostingProvider(), - response.isPublicProxy(), - response.isResidentialProxy(), - response.isTorExitNode(), - network, - response.ipRisk() - ); - } - /** * @return The IP address that the data in the model is for. * @deprecated Use {@link #ipAddress()} instead. This method will be removed in 6.0.0. diff --git a/src/main/java/com/maxmind/geoip2/model/IspResponse.java b/src/main/java/com/maxmind/geoip2/model/IspResponse.java index eca1a7618..73a064ca8 100644 --- a/src/main/java/com/maxmind/geoip2/model/IspResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IspResponse.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -40,7 +42,7 @@ public record IspResponse( String autonomousSystemOrganization, @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("isp") @@ -61,34 +63,10 @@ public record IspResponse( @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network ) implements JsonSerializable { - /** - * Constructs an instance of {@code IspResponse}. - * - * @param response The {@code AsnResponse} object to copy. - * @param ipAddress The IP address that the data in the model is for. - * @param network The network associated with the record. - */ - public IspResponse( - IspResponse response, - String ipAddress, - Network network - ) { - this( - response.autonomousSystemNumber(), - response.autonomousSystemOrganization(), - ipAddress, - response.isp(), - response.mobileCountryCode(), - response.mobileNetworkCode(), - response.organization(), - network - ); - } - /** * @return The autonomous system number associated with the IP address. * @deprecated Use {@link #autonomousSystemNumber()} instead. This method will be removed diff --git a/src/main/java/com/maxmind/geoip2/record/Traits.java b/src/main/java/com/maxmind/geoip2/record/Traits.java index 825e03cc8..63263f007 100644 --- a/src/main/java/com/maxmind/geoip2/record/Traits.java +++ b/src/main/java/com/maxmind/geoip2/record/Traits.java @@ -4,7 +4,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.maxmind.db.MaxMindDbConstructor; +import com.maxmind.db.MaxMindDbIpAddress; +import com.maxmind.db.MaxMindDbNetwork; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; @@ -89,7 +90,7 @@ public record Traits( String domain, @JsonProperty("ip_address") - @MaxMindDbParameter(name = "ip_address") + @MaxMindDbIpAddress String ipAddress, @JsonProperty("is_anonymous") @@ -139,7 +140,7 @@ public record Traits( @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) @JsonSerialize(using = ToStringSerializer.class) - @MaxMindDbParameter(name = "network") + @MaxMindDbNetwork Network network, @JsonProperty("organization") @@ -169,139 +170,6 @@ public Traits() { null, null, null, null, null, null, null); } - /** - * Constructs an instance of {@code Traits}. - * - * @param ipAddress the IP address - * @param network the network - */ - public Traits(String ipAddress, Network network) { - this(null, null, (ConnectionType) null, null, - ipAddress, false, false, false, false, - false, false, false, false, null, - null, null, network, null, null, null, null); - } - - /** - * Constructs an instance of {@code Traits} from MaxMind database with type conversions. - * - * @param autonomousSystemNumber the autonomous system number - * @param autonomousSystemOrganization the autonomous system organization - * @param connectionType the connection type as a string - * @param domain the domain - * @param ipAddress the IP address - * @param isAnonymous the anonymous flag - * @param isAnonymousVpn the anonymous VPN flag - * @param isAnycast the anycast flag - * @param isHostingProvider the hosting provider flag - * @param isLegitimateProxy the legitimate proxy flag - * @param isPublicProxy the public proxy flag - * @param isResidentialProxy the residential proxy flag - * @param isTorExitNode the Tor exit node flag - * @param isp the ISP - * @param mobileCountryCode the mobile country code - * @param mobileNetworkCode the mobile network code - * @param network the network - * @param organization the organization - * @param userType the user type - * @param userCount the user count - * @param staticIpScore the static IP score - */ - @MaxMindDbConstructor - public Traits( - @MaxMindDbParameter(name = "autonomous_system_number") Long autonomousSystemNumber, - @MaxMindDbParameter(name = "autonomous_system_organization") - String autonomousSystemOrganization, - @MaxMindDbParameter(name = "connection_type") String connectionType, - @MaxMindDbParameter(name = "domain") String domain, - @MaxMindDbParameter(name = "ip_address") String ipAddress, - @MaxMindDbParameter(name = "is_anonymous", useDefault = true) - boolean isAnonymous, - @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) - boolean isAnonymousVpn, - @MaxMindDbParameter(name = "is_anycast", useDefault = true) - boolean isAnycast, - @MaxMindDbParameter(name = "is_hosting_provider", useDefault = true) - boolean isHostingProvider, - @MaxMindDbParameter(name = "is_legitimate_proxy", useDefault = true) - boolean isLegitimateProxy, - @MaxMindDbParameter(name = "is_public_proxy", useDefault = true) - boolean isPublicProxy, - @MaxMindDbParameter(name = "is_residential_proxy", useDefault = true) - boolean isResidentialProxy, - @MaxMindDbParameter(name = "is_tor_exit_node", useDefault = true) - boolean isTorExitNode, - @MaxMindDbParameter(name = "isp") String isp, - @MaxMindDbParameter(name = "mobile_country_code") String mobileCountryCode, - @MaxMindDbParameter(name = "mobile_network_code") String mobileNetworkCode, - @MaxMindDbParameter(name = "network") Network network, - @MaxMindDbParameter(name = "organization") String organization, - @MaxMindDbParameter(name = "user_type") String userType, - @MaxMindDbParameter(name = "user_count") Integer userCount, - @MaxMindDbParameter(name = "static_ip_score") Double staticIpScore - ) { - this( - autonomousSystemNumber, - autonomousSystemOrganization, - ConnectionType.fromString(connectionType), - domain, - ipAddress, - isAnonymous, - isAnonymousVpn, - isAnycast, - isHostingProvider, - isLegitimateProxy, - isPublicProxy, - isResidentialProxy, - isTorExitNode, - isp, - mobileCountryCode, - mobileNetworkCode, - network, - organization, - userType, - userCount, - staticIpScore - ); - } - - /** - * Constructs an instance of {@code Traits}. - * - * @param traits the traits - * @param ipAddress the IP address - * @param network the network - */ - public Traits( - Traits traits, - String ipAddress, - Network network - ) { - this( - traits.autonomousSystemNumber(), - traits.autonomousSystemOrganization(), - traits.connectionType(), - traits.domain(), - ipAddress, - traits.isAnonymous(), - traits.isAnonymousVpn(), - traits.isAnycast(), - traits.isHostingProvider(), - traits.isLegitimateProxy(), - traits.isPublicProxy(), - traits.isResidentialProxy(), - traits.isTorExitNode(), - traits.isp(), - traits.mobileCountryCode(), - traits.mobileNetworkCode(), - network, - traits.organization(), - traits.userType(), - traits.userCount(), - traits.staticIpScore() - ); - } - /** * @return The Date: Thu, 23 Oct 2025 10:33:18 -0700 Subject: [PATCH 06/17] Use generics to eliminate redundant private getX methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the private getX methods in DatabaseReader followed similar patterns, so we've consolidated them into a single generic getResponse() method. For responses that need locale transformations (City, Country, Enterprise), we use Optional.map() to apply the transformation. This also changes the get() method to accept an explicit caller parameter instead of introspecting the stack trace, making error messages clearer and more reliable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../com/maxmind/geoip2/DatabaseReader.java | 223 +++++------------- 1 file changed, 61 insertions(+), 162 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index bf2bc7dd0..6cb268fdb 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -244,16 +244,15 @@ static record LookupResult(T model, String ipAddress, Network network) { * @param ipAddress IPv4 or IPv6 address to lookup. * @param cls The class to deserialize to. * @param expectedType The expected database type. + * @param caller The name of the public method calling this (for error messages). * @return A {@code LookupResult} object with the data for the IP address * @throws IOException if there is an error opening or reading from the file. */ private LookupResult get(InetAddress ipAddress, Class cls, - DatabaseType expectedType) + DatabaseType expectedType, String caller) throws IOException { if ((databaseType & expectedType.type) == 0) { - String caller = Thread.currentThread().getStackTrace()[3] - .getMethodName(); throw new UnsupportedOperationException( "Invalid attempt to open a " + metadata().databaseType() + " database using the " + caller + " method"); @@ -266,6 +265,30 @@ private LookupResult get(InetAddress ipAddress, Class cls, return new LookupResult<>(o, ipAddress.getHostAddress(), record.network()); } + /** + * Generic method to get a response. + * + * @param ipAddress IPv4 or IPv6 address to lookup. + * @param cls The class to deserialize to. + * @param expectedType The expected database type. + * @param caller The name of the public method calling this (for error messages). + * @return An Optional containing the response, or empty if not found + * @throws IOException if there is an error opening or reading from the file. + */ + private Optional getResponse( + InetAddress ipAddress, + Class cls, + DatabaseType expectedType, + String caller + ) throws IOException { + LookupResult result = this.get(ipAddress, cls, expectedType, caller); + T response = result.model(); + if (response == null) { + return Optional.empty(); + } + return Optional.of(response); + } + /** *

* Closes the database. @@ -288,7 +311,7 @@ public void close() throws IOException { @Override public CountryResponse country(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getCountry(ipAddress); + Optional r = tryCountry(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -299,33 +322,19 @@ public CountryResponse country(InetAddress ipAddress) throws IOException, @Override public Optional tryCountry(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getCountry(ipAddress); - } - - private Optional getCountry( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + Optional response = getResponse( ipAddress, CountryResponse.class, - DatabaseType.COUNTRY - ); - CountryResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of( - new CountryResponse( - response, - locales - ) + DatabaseType.COUNTRY, + "country" ); + return response.map(r -> new CountryResponse(r, locales)); } @Override public CityResponse city(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getCity(ipAddress); + Optional r = tryCity(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -336,24 +345,13 @@ public CityResponse city(InetAddress ipAddress) throws IOException, @Override public Optional tryCity(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getCity(ipAddress); - } - - private Optional getCity( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + Optional response = getResponse( ipAddress, CityResponse.class, - DatabaseType.CITY - ); - CityResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of( - new CityResponse(response, locales) + DatabaseType.CITY, + "city" ); + return response.map(r -> new CityResponse(r, locales)); } /** @@ -367,7 +365,7 @@ private Optional getCity( @Override public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getAnonymousIp(ipAddress); + Optional r = tryAnonymousIp(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -378,22 +376,12 @@ public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException @Override public Optional tryAnonymousIp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getAnonymousIp(ipAddress); - } - - private Optional getAnonymousIp( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + return getResponse( ipAddress, AnonymousIpResponse.class, - DatabaseType.ANONYMOUS_IP + DatabaseType.ANONYMOUS_IP, + "anonymousIp" ); - AnonymousIpResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); } /** @@ -407,7 +395,7 @@ private Optional getAnonymousIp( @Override public AnonymousPlusResponse anonymousPlus(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getAnonymousPlus(ipAddress); + Optional r = tryAnonymousPlus(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -419,22 +407,12 @@ public AnonymousPlusResponse anonymousPlus(InetAddress ipAddress) throws IOExcep public Optional tryAnonymousPlus(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getAnonymousPlus(ipAddress); - } - - private Optional getAnonymousPlus( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + return getResponse( ipAddress, AnonymousPlusResponse.class, - DatabaseType.ANONYMOUS_PLUS + DatabaseType.ANONYMOUS_PLUS, + "anonymousPlus" ); - AnonymousPlusResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); } @@ -449,7 +427,7 @@ private Optional getAnonymousPlus( @Override public IpRiskResponse ipRisk(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getIpRisk(ipAddress); + Optional r = tryIpRisk(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -460,21 +438,7 @@ public IpRiskResponse ipRisk(InetAddress ipAddress) throws IOException, @Override public Optional tryIpRisk(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getIpRisk(ipAddress); - } - - private Optional getIpRisk(InetAddress ipAddress) throws IOException, - GeoIp2Exception { - LookupResult result = this.get( - ipAddress, - IpRiskResponse.class, - DatabaseType.IP_RISK - ); - IpRiskResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); + return getResponse(ipAddress, IpRiskResponse.class, DatabaseType.IP_RISK, "ipRisk"); } /** @@ -488,7 +452,7 @@ private Optional getIpRisk(InetAddress ipAddress) throws IOExcep @Override public AsnResponse asn(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getAsn(ipAddress); + Optional r = tryAsn(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -499,21 +463,7 @@ public AsnResponse asn(InetAddress ipAddress) throws IOException, @Override public Optional tryAsn(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getAsn(ipAddress); - } - - private Optional getAsn(InetAddress ipAddress) - throws IOException, GeoIp2Exception { - LookupResult result = this.get( - ipAddress, - AsnResponse.class, - DatabaseType.ASN - ); - AsnResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); + return getResponse(ipAddress, AsnResponse.class, DatabaseType.ASN, "asn"); } /** @@ -527,7 +477,7 @@ private Optional getAsn(InetAddress ipAddress) @Override public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getConnectionType(ipAddress); + Optional r = tryConnectionType(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -538,22 +488,12 @@ public ConnectionTypeResponse connectionType(InetAddress ipAddress) @Override public Optional tryConnectionType(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getConnectionType(ipAddress); - } - - private Optional getConnectionType( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + return getResponse( ipAddress, ConnectionTypeResponse.class, - DatabaseType.CONNECTION_TYPE + DatabaseType.CONNECTION_TYPE, + "connectionType" ); - ConnectionTypeResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); } /** @@ -567,7 +507,7 @@ private Optional getConnectionType( @Override public DomainResponse domain(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getDomain(ipAddress); + Optional r = tryDomain(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -578,22 +518,7 @@ public DomainResponse domain(InetAddress ipAddress) throws IOException, @Override public Optional tryDomain(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getDomain(ipAddress); - } - - private Optional getDomain( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( - ipAddress, - DomainResponse.class, - DatabaseType.DOMAIN - ); - DomainResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); + return getResponse(ipAddress, DomainResponse.class, DatabaseType.DOMAIN, "domain"); } /** @@ -607,7 +532,7 @@ private Optional getDomain( @Override public EnterpriseResponse enterprise(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getEnterprise(ipAddress); + Optional r = tryEnterprise(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -618,24 +543,13 @@ public EnterpriseResponse enterprise(InetAddress ipAddress) throws IOException, @Override public Optional tryEnterprise(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getEnterprise(ipAddress); - } - - private Optional getEnterprise( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( + Optional response = getResponse( ipAddress, EnterpriseResponse.class, - DatabaseType.ENTERPRISE - ); - EnterpriseResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of( - new EnterpriseResponse(response, locales) + DatabaseType.ENTERPRISE, + "enterprise" ); + return response.map(r -> new EnterpriseResponse(r, locales)); } /** @@ -649,7 +563,7 @@ private Optional getEnterprise( @Override public IspResponse isp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = getIsp(ipAddress); + Optional r = tryIsp(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -660,22 +574,7 @@ public IspResponse isp(InetAddress ipAddress) throws IOException, @Override public Optional tryIsp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - return getIsp(ipAddress); - } - - private Optional getIsp( - InetAddress ipAddress - ) throws IOException, GeoIp2Exception { - LookupResult result = this.get( - ipAddress, - IspResponse.class, - DatabaseType.ISP - ); - IspResponse response = result.model(); - if (response == null) { - return Optional.empty(); - } - return Optional.of(response); + return getResponse(ipAddress, IspResponse.class, DatabaseType.ISP, "isp"); } /** From 9429631d2a39c05a3069d1c38a54f490dcf51321 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 11:00:17 -0700 Subject: [PATCH 07/17] Change ipAddress from String to InetAddress in all models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the ipAddress field type from String to InetAddress across all model and record classes. This provides better type safety and makes it clear that these fields contain IP addresses. Key changes: - Updated ipAddress field type to InetAddress in all response/record classes - Deprecated getIpAddress() methods now call ipAddress().getHostAddress() to maintain backward compatibility (still return String) - Created InetAddressSerializer to serialize InetAddress to String in JSON - Created InetAddressDeserializer to deserialize JSON strings to InetAddress - Created InetAddressModule and registered it globally in JsonSerializable - Updated all tests to call .getHostAddress() when comparing IP addresses The new ipAddress() accessor returns InetAddress, while the deprecated getIpAddress() continues to return String for backward compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../geoip2/InetAddressDeserializer.java | 34 +++++++++++++++++++ .../com/maxmind/geoip2/InetAddressModule.java | 18 ++++++++++ .../maxmind/geoip2/InetAddressSerializer.java | 29 ++++++++++++++++ .../com/maxmind/geoip2/JsonSerializable.java | 1 + .../geoip2/model/AnonymousIpResponse.java | 5 +-- .../geoip2/model/AnonymousPlusResponse.java | 7 ++-- .../com/maxmind/geoip2/model/AsnResponse.java | 5 +-- .../geoip2/model/ConnectionTypeResponse.java | 5 +-- .../maxmind/geoip2/model/DomainResponse.java | 5 +-- .../maxmind/geoip2/model/IpRiskResponse.java | 5 +-- .../com/maxmind/geoip2/model/IspResponse.java | 5 +-- .../com/maxmind/geoip2/record/Traits.java | 5 +-- .../maxmind/geoip2/DatabaseReaderTest.java | 20 +++++------ .../maxmind/geoip2/WebServiceClientTest.java | 8 ++--- .../geoip2/model/CountryResponseTest.java | 2 +- .../geoip2/model/InsightsResponseTest.java | 2 +- .../com/maxmind/geoip2/model/JsonTest.java | 1 + 17 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java create mode 100644 src/main/java/com/maxmind/geoip2/InetAddressModule.java create mode 100644 src/main/java/com/maxmind/geoip2/InetAddressSerializer.java diff --git a/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java b/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java new file mode 100644 index 000000000..7bc007a3d --- /dev/null +++ b/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java @@ -0,0 +1,34 @@ +package com.maxmind.geoip2; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Deserializes a string to an InetAddress. + */ +public class InetAddressDeserializer extends StdDeserializer { + /** + * Constructs an instance of {@code InetAddressDeserializer}. + */ + public InetAddressDeserializer() { + super(InetAddress.class); + } + + @Override + public InetAddress deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + String value = p.getValueAsString(); + if (value == null || value.isEmpty()) { + return null; + } + try { + return InetAddress.getByName(value); + } catch (UnknownHostException e) { + throw new IOException("Invalid IP address: " + value, e); + } + } +} diff --git a/src/main/java/com/maxmind/geoip2/InetAddressModule.java b/src/main/java/com/maxmind/geoip2/InetAddressModule.java new file mode 100644 index 000000000..6b989a4a4 --- /dev/null +++ b/src/main/java/com/maxmind/geoip2/InetAddressModule.java @@ -0,0 +1,18 @@ +package com.maxmind.geoip2; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.net.InetAddress; + +/** + * Jackson module for InetAddress serialization and deserialization. + */ +public class InetAddressModule extends SimpleModule { + /** + * Constructs an instance of {@code InetAddressModule}. + */ + public InetAddressModule() { + super("InetAddressModule"); + addSerializer(InetAddress.class, new InetAddressSerializer()); + addDeserializer(InetAddress.class, new InetAddressDeserializer()); + } +} diff --git a/src/main/java/com/maxmind/geoip2/InetAddressSerializer.java b/src/main/java/com/maxmind/geoip2/InetAddressSerializer.java new file mode 100644 index 000000000..38319841c --- /dev/null +++ b/src/main/java/com/maxmind/geoip2/InetAddressSerializer.java @@ -0,0 +1,29 @@ +package com.maxmind.geoip2; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; +import java.net.InetAddress; + +/** + * Serializes InetAddress to its host address string representation. + */ +public class InetAddressSerializer extends StdSerializer { + /** + * Constructs an instance of {@code InetAddressSerializer}. + */ + public InetAddressSerializer() { + super(InetAddress.class); + } + + @Override + public void serialize(InetAddress value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + if (value == null) { + gen.writeNull(); + } else { + gen.writeString(value.getHostAddress()); + } + } +} diff --git a/src/main/java/com/maxmind/geoip2/JsonSerializable.java b/src/main/java/com/maxmind/geoip2/JsonSerializable.java index 46dd37ac9..190553cda 100644 --- a/src/main/java/com/maxmind/geoip2/JsonSerializable.java +++ b/src/main/java/com/maxmind/geoip2/JsonSerializable.java @@ -21,6 +21,7 @@ default String toJson() throws IOException { JsonMapper mapper = JsonMapper.builder() .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) .addModule(new JavaTimeModule()) + .addModule(new InetAddressModule()) .serializationInclusion(JsonInclude.Include.NON_NULL) .serializationInclusion(JsonInclude.Include.NON_EMPTY) .build(); diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java index eb6104018..c17fee07b 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java @@ -11,6 +11,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoIP2 Anonymous IP model. @@ -32,7 +33,7 @@ public record AnonymousIpResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("is_anonymous") @MaxMindDbParameter(name = "is_anonymous", useDefault = true) @@ -71,7 +72,7 @@ public record AnonymousIpResponse( @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java index 4a637eee3..e6be23a9e 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java @@ -12,6 +12,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; import java.time.LocalDate; /** @@ -40,7 +41,7 @@ public record AnonymousPlusResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("is_anonymous") @MaxMindDbParameter(name = "is_anonymous", useDefault = true) @@ -102,7 +103,7 @@ public record AnonymousPlusResponse( */ @MaxMindDbConstructor public AnonymousPlusResponse( - @MaxMindDbIpAddress String ipAddress, + @MaxMindDbIpAddress InetAddress ipAddress, @MaxMindDbParameter(name = "is_anonymous", useDefault = true) boolean isAnonymous, @MaxMindDbParameter(name = "is_anonymous_vpn", useDefault = true) @@ -142,7 +143,7 @@ public AnonymousPlusResponse( @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java index c2bcea216..22bae1b04 100644 --- a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java @@ -11,6 +11,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoLite2 ASN model. @@ -33,7 +34,7 @@ public record AsnResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) @@ -71,7 +72,7 @@ public String getAutonomousSystemOrganization() { @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java index 3c7482347..30bef27cd 100644 --- a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java @@ -14,6 +14,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoIP2 Connection-Type model. @@ -30,7 +31,7 @@ public record ConnectionTypeResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) @@ -105,7 +106,7 @@ public ConnectionType getConnectionType() { @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java index c43a9cbaa..d721a930c 100644 --- a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java @@ -11,6 +11,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoIP2 Domain model. @@ -28,7 +29,7 @@ public record DomainResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("network") @JsonDeserialize(using = NetworkDeserializer.class) @@ -54,7 +55,7 @@ public String getDomain() { @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index 8c2679468..bbab36dd3 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -11,6 +11,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoIP2 IP Risk model. @@ -33,7 +34,7 @@ public record IpRiskResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("is_anonymous") @MaxMindDbParameter(name = "is_anonymous", useDefault = true) @@ -76,7 +77,7 @@ public record IpRiskResponse( @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/model/IspResponse.java b/src/main/java/com/maxmind/geoip2/model/IspResponse.java index 73a064ca8..7056397ef 100644 --- a/src/main/java/com/maxmind/geoip2/model/IspResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IspResponse.java @@ -11,6 +11,7 @@ import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; +import java.net.InetAddress; /** * This class provides the GeoIP2 ISP model. @@ -43,7 +44,7 @@ public record IspResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("isp") @MaxMindDbParameter(name = "isp") @@ -97,7 +98,7 @@ public String getAutonomousSystemOrganization() { @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/main/java/com/maxmind/geoip2/record/Traits.java b/src/main/java/com/maxmind/geoip2/record/Traits.java index 63263f007..2540b9d0f 100644 --- a/src/main/java/com/maxmind/geoip2/record/Traits.java +++ b/src/main/java/com/maxmind/geoip2/record/Traits.java @@ -11,6 +11,7 @@ import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.NetworkDeserializer; import com.maxmind.geoip2.model.ConnectionTypeResponse.ConnectionType; +import java.net.InetAddress; /** * Contains data for the traits record associated with an IP address. @@ -91,7 +92,7 @@ public record Traits( @JsonProperty("ip_address") @MaxMindDbIpAddress - String ipAddress, + InetAddress ipAddress, @JsonProperty("is_anonymous") @MaxMindDbParameter(name = "is_anonymous", useDefault = true) @@ -261,7 +262,7 @@ public String getDomain() { @Deprecated(since = "5.0.0", forRemoval = true) @JsonProperty("ip_address") public String getIpAddress() { - return ipAddress(); + return ipAddress().getHostAddress(); } /** diff --git a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java index 060219e4c..b92e1a1c4 100644 --- a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java +++ b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java @@ -156,7 +156,7 @@ public void hasIpAddressURL() throws Exception { private void hasIpInfo(DatabaseReader reader) throws IOException, GeoIp2Exception { CityResponse cio = reader.city(InetAddress.getByName("81.2.69.160")); - assertEquals("81.2.69.160", cio.traits().ipAddress()); + assertEquals("81.2.69.160", cio.traits().ipAddress().getHostAddress()); assertEquals("81.2.69.160/27", cio.traits().network().toString()); } @@ -220,7 +220,7 @@ public void testAnonymousIp() throws Exception { assertFalse(response.isPublicProxy()); assertFalse(response.isResidentialProxy()); assertFalse(response.isTorExitNode()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.2.0.0/16", response.network().toString()); AnonymousIpResponse tryResponse = reader.tryAnonymousIp(ipAddress).get(); @@ -242,7 +242,7 @@ public void testAnonymousPlus() throws Exception { assertFalse(response.isPublicProxy()); assertFalse(response.isResidentialProxy()); assertFalse(response.isTorExitNode()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.2.0.1/32", response.network().toString()); assertEquals("2025-04-14", response.networkLastSeen().toString()); assertEquals("foo", response.providerName()); @@ -273,7 +273,7 @@ public void testAsn() throws Exception { assertEquals(1221, response.autonomousSystemNumber().intValue()); assertEquals("Telstra Pty Ltd", response.autonomousSystemOrganization()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.128.0.0/11", response.network().toString()); AsnResponse tryResponse = reader.tryAsn(ipAddress).get(); @@ -292,7 +292,7 @@ public void testCity() throws Exception { assertEquals(2635167, response.country().geonameId().intValue()); assertEquals(100, response.location().accuracyRadius().intValue()); assertFalse(response.traits().isLegitimateProxy()); - assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress().getHostAddress()); assertEquals("81.2.69.192/28", response.traits().network().toString()); CityResponse tryResponse = reader.tryCity(ipAddress).get(); @@ -318,7 +318,7 @@ public void testConnectionType() throws Exception { ConnectionTypeResponse response = reader.connectionType(ipAddress); assertEquals(ConnectionType.CELLULAR, response.connectionType()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.0.1.0/24", response.network().toString()); ConnectionTypeResponse tryResponse = reader.tryConnectionType(ipAddress).get(); @@ -337,7 +337,7 @@ public void testCountry() throws Exception { assertEquals("NA", response.continent().code()); assertEquals(6252001, response.country().geonameId().intValue()); assertEquals(6252001, response.registeredCountry().geonameId().intValue()); - assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress().getHostAddress()); assertEquals("74.209.16.0/20", response.traits().network().toString()); CountryResponse tryResponse = reader.tryCountry(ipAddress).get(); @@ -357,7 +357,7 @@ public void testDomain() throws Exception { InetAddress ipAddress = InetAddress.getByName("1.2.0.0"); DomainResponse response = reader.domain(ipAddress); assertEquals("maxmind.com", response.domain()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.2.0.0/16", response.network().toString()); DomainResponse tryResponse = reader.tryDomain(ipAddress).get(); @@ -379,7 +379,7 @@ public void testEnterprise() throws Exception { assertEquals(27, response.location().accuracyRadius().intValue()); assertEquals(ConnectionType.CABLE_DSL, response.traits().connectionType()); assertTrue(response.traits().isLegitimateProxy()); - assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.traits().ipAddress().getHostAddress()); assertEquals("74.209.16.0/20", response.traits().network().toString()); EnterpriseResponse tryResponse = reader.tryEnterprise(ipAddress).get(); @@ -414,7 +414,7 @@ public void testIsp() throws Exception { assertEquals("Telstra Internet", response.isp()); assertEquals("Telstra Internet", response.organization()); - assertEquals(ipAddress.getHostAddress(), response.ipAddress()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); assertEquals("1.128.0.0/11", response.network().toString()); IspResponse tryResponse = reader.tryIsp(ipAddress).get(); diff --git a/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java b/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java index 4e0f49546..cd5c2af01 100644 --- a/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java +++ b/src/test/java/com/maxmind/geoip2/WebServiceClientTest.java @@ -135,7 +135,7 @@ public void test200WithDefaultValues() throws Exception { assertNull(traits.autonomousSystemOrganization()); assertNull(traits.connectionType()); assertNull(traits.domain()); - assertEquals("1.2.3.13", traits.ipAddress()); + assertEquals("1.2.3.13", traits.ipAddress().getHostAddress()); assertEquals("1.2.3.0/24", traits.network().toString()); assertNull(traits.isp()); assertNull(traits.organization()); @@ -179,7 +179,7 @@ public void test200OnInsightsAsMe() throws Exception { WebServiceClient client = createSuccessClient("insights", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.insights().traits().ipAddress()); + client.insights().traits().ipAddress().getHostAddress()); } @Test @@ -187,7 +187,7 @@ public void test200OnCityAsMe() throws Exception { WebServiceClient client = createSuccessClient("city", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.city().traits().ipAddress()); + client.city().traits().ipAddress().getHostAddress()); } @Test @@ -195,7 +195,7 @@ public void test200OnCountryAsMe() throws Exception { WebServiceClient client = createSuccessClient("country", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", - client.country().traits().ipAddress()); + client.country().traits().ipAddress().getHostAddress()); } @Test diff --git a/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java b/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java index a60a2b0c1..d0a7c415b 100644 --- a/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/CountryResponseTest.java @@ -151,7 +151,7 @@ public void testTraits() { assertEquals( "1.2.3.4", - this.country.traits().ipAddress(), + this.country.traits().ipAddress().getHostAddress(), "country.traits().getIpAddress does not return 1.2.3.4" ); diff --git a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java index ccb1716ef..ae76aed2c 100644 --- a/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java +++ b/src/test/java/com/maxmind/geoip2/model/InsightsResponseTest.java @@ -134,7 +134,7 @@ public void testTraits() { ); assertEquals( "1.2.3.4", - traits.ipAddress(), + traits.ipAddress().getHostAddress(), "traits.ipAddress() does not return 1.2.3.4" ); assertTrue(traits.isAnonymous(), "traits.isAnonymous() returns true"); diff --git a/src/test/java/com/maxmind/geoip2/model/JsonTest.java b/src/test/java/com/maxmind/geoip2/model/JsonTest.java index cd8109e52..73aab8da8 100644 --- a/src/test/java/com/maxmind/geoip2/model/JsonTest.java +++ b/src/test/java/com/maxmind/geoip2/model/JsonTest.java @@ -334,6 +334,7 @@ public void testIspSerialization() throws Exception { throws IOException { JsonMapper mapper = JsonMapper.builder() .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) + .addModule(new com.maxmind.geoip2.InetAddressModule()) .build(); InjectableValues inject = new InjectableValues.Std().addValue( "locales", Collections.singletonList("en")); From f44dfd80ceacb9177cfdac511e88bc2078702ad4 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 11:44:43 -0700 Subject: [PATCH 08/17] Remove unused imports --- src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java | 1 - .../java/com/maxmind/geoip2/model/AnonymousPlusResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/AsnResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/CityResponse.java | 2 -- .../java/com/maxmind/geoip2/model/ConnectionTypeResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/CountryResponse.java | 2 -- src/main/java/com/maxmind/geoip2/model/DomainResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java | 2 -- src/main/java/com/maxmind/geoip2/model/InsightsResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java | 1 - src/main/java/com/maxmind/geoip2/model/IspResponse.java | 1 - src/main/java/com/maxmind/geoip2/record/City.java | 2 -- 12 files changed, 16 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java index c17fee07b..c8a222269 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousIpResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java index e6be23a9e..22ac498bb 100644 --- a/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AnonymousPlusResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java index 22bae1b04..0dea5f03f 100644 --- a/src/main/java/com/maxmind/geoip2/model/AsnResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/AsnResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/model/CityResponse.java b/src/main/java/com/maxmind/geoip2/model/CityResponse.java index 0ba099198..65a94673d 100644 --- a/src/main/java/com/maxmind/geoip2/model/CityResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CityResponse.java @@ -1,10 +1,8 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbParameter; -import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; diff --git a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java index 30bef27cd..40b492c9c 100644 --- a/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/ConnectionTypeResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java index 45bf4186e..23c3c3f2a 100644 --- a/src/main/java/com/maxmind/geoip2/model/CountryResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CountryResponse.java @@ -1,9 +1,7 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbParameter; -import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; diff --git a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java index d721a930c..347f255c0 100644 --- a/src/main/java/com/maxmind/geoip2/model/DomainResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/DomainResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java index 8855b34a1..386ce6a6e 100644 --- a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java @@ -1,10 +1,8 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbParameter; -import com.maxmind.db.Network; import com.maxmind.geoip2.JsonSerializable; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; diff --git a/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java b/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java index 7196c5f92..2358153ed 100644 --- a/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/InsightsResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.geoip2.JsonSerializable; diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index bbab36dd3..4795ae187 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/model/IspResponse.java b/src/main/java/com/maxmind/geoip2/model/IspResponse.java index 7056397ef..612816b48 100644 --- a/src/main/java/com/maxmind/geoip2/model/IspResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IspResponse.java @@ -1,6 +1,5 @@ package com.maxmind.geoip2.model; -import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/src/main/java/com/maxmind/geoip2/record/City.java b/src/main/java/com/maxmind/geoip2/record/City.java index e8902dcb2..2ba14b2c7 100644 --- a/src/main/java/com/maxmind/geoip2/record/City.java +++ b/src/main/java/com/maxmind/geoip2/record/City.java @@ -4,8 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.maxmind.db.MaxMindDbParameter; import com.maxmind.geoip2.NamedRecord; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; From f5299b9cc24848916c5fea54eb441d20a730aa68 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 12:05:07 -0700 Subject: [PATCH 09/17] Remove outdated distributionManagement section --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 1ad623533..7b2efa971 100644 --- a/pom.xml +++ b/pom.xml @@ -294,10 +294,4 @@ - - - sonatype-nexus-staging - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - From cb99a66165e35f59d1eba3d826240be88aa271b4 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 12:08:50 -0700 Subject: [PATCH 10/17] Prepare for a snapshot release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7b2efa971..d85bfca90 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.maxmind.geoip2 geoip2 - 4.4.0 + 5.0.0-SNAPSHOT jar MaxMind GeoIP2 API GeoIP2 webservice client and database reader From 9309fdce6940c9f7092a911a011f85fc3e9f0d7a Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 13:19:12 -0700 Subject: [PATCH 11/17] Fix documentation typos in record getters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected typos and style inconsistencies in deprecated getter javadocs to match the param documentation: - MaxMind: "queried" → "queries" - Subdivision.getConfidence(): "This is a value" → "A value" - Subdivision.getIsoCode(): "contain" → "containing", "3166-2code" → "3166-2 code" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/main/java/com/maxmind/geoip2/record/MaxMind.java | 2 +- src/main/java/com/maxmind/geoip2/record/Subdivision.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/record/MaxMind.java b/src/main/java/com/maxmind/geoip2/record/MaxMind.java index 18a0307a0..a9380b89d 100644 --- a/src/main/java/com/maxmind/geoip2/record/MaxMind.java +++ b/src/main/java/com/maxmind/geoip2/record/MaxMind.java @@ -26,7 +26,7 @@ public MaxMind() { } /** - * @return The number of remaining queried in your account for the current + * @return The number of remaining queries in your account for the current * web service. This returns {@code null} when called on a database. * @deprecated Use {@link #queriesRemaining()} instead. This method will be removed in 6.0.0. */ diff --git a/src/main/java/com/maxmind/geoip2/record/Subdivision.java b/src/main/java/com/maxmind/geoip2/record/Subdivision.java index 31c5c3fac..460fc6f11 100644 --- a/src/main/java/com/maxmind/geoip2/record/Subdivision.java +++ b/src/main/java/com/maxmind/geoip2/record/Subdivision.java @@ -83,7 +83,7 @@ public Subdivision( } /** - * @return This is a value from 0-100 indicating MaxMind's confidence that + * @return A value from 0-100 indicating MaxMind's confidence that * the subdivision is correct. This attribute is only available from * the Insights web service and the GeoIP2 Enterprise database. * @deprecated Use {@link #confidence()} instead. This method will be removed in 6.0.0. @@ -95,10 +95,10 @@ public Integer getConfidence() { } /** - * @return This is a string up to three characters long contain the + * @return A string up to three characters long containing the * subdivision portion of the ISO - * 3166-2code. + * 3166-2 code. * @deprecated Use {@link #isoCode()} instead. This method will be removed in 6.0.0. */ @Deprecated(since = "5.0.0", forRemoval = true) From 9d5369d44a056fb0f2e190f5aa1d77f727f75142 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 23 Oct 2025 13:57:38 -0700 Subject: [PATCH 12/17] Add repository configuration for consuming snapshot releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables Maven to resolve snapshot dependencies from the Central Portal. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pom.xml b/pom.xml index d85bfca90..9afd3724e 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,19 @@ goschwald@maxmind.com + + + Central Portal Snapshots + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + com.maxmind.db From 33c09f228fb968eb6fb92a1a281f50088e94dde5 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 24 Oct 2025 06:56:34 -0700 Subject: [PATCH 13/17] Replace explicit types with var for local variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This modernizes the codebase by using Java's var keyword for local variables where the type is obvious from the initializer. This includes: - Local variable declarations with initializers - Enhanced for-loop variables - Try-with-resources variables Some try-block variables were refactored into helper methods to enable var usage (parsePrefixLength in NetworkDeserializer and readBody in WebServiceClient). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../com/maxmind/geoip2/DatabaseReader.java | 38 ++++++------- .../geoip2/InetAddressDeserializer.java | 2 +- .../java/com/maxmind/geoip2/NamedRecord.java | 2 +- .../maxmind/geoip2/NetworkDeserializer.java | 27 +++++---- .../com/maxmind/geoip2/WebServiceClient.java | 55 ++++++++++--------- .../maxmind/geoip2/model/CityResponse.java | 4 +- .../geoip2/model/EnterpriseResponse.java | 4 +- 7 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index 6cb268fdb..cf7a7e7c2 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -115,8 +115,8 @@ private DatabaseReader(Builder builder) throws IOException { } private int getDatabaseType() { - String databaseType = this.metadata().databaseType(); - int type = 0; + var databaseType = this.metadata().databaseType(); + var type = 0; if (databaseType.contains("GeoIP2-Anonymous-IP")) { type |= DatabaseType.ANONYMOUS_IP.type; } @@ -258,9 +258,9 @@ private LookupResult get(InetAddress ipAddress, Class cls, + " database using the " + caller + " method"); } - DatabaseRecord record = reader.getRecord(ipAddress, cls); + var record = reader.getRecord(ipAddress, cls); - T o = record.data(); + var o = record.data(); return new LookupResult<>(o, ipAddress.getHostAddress(), record.network()); } @@ -281,8 +281,8 @@ private Optional getResponse( DatabaseType expectedType, String caller ) throws IOException { - LookupResult result = this.get(ipAddress, cls, expectedType, caller); - T response = result.model(); + var result = this.get(ipAddress, cls, expectedType, caller); + var response = result.model(); if (response == null) { return Optional.empty(); } @@ -311,7 +311,7 @@ public void close() throws IOException { @Override public CountryResponse country(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryCountry(ipAddress); + var r = tryCountry(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -322,7 +322,7 @@ public CountryResponse country(InetAddress ipAddress) throws IOException, @Override public Optional tryCountry(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional response = getResponse( + var response = getResponse( ipAddress, CountryResponse.class, DatabaseType.COUNTRY, @@ -334,7 +334,7 @@ public Optional tryCountry(InetAddress ipAddress) throws IOExce @Override public CityResponse city(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryCity(ipAddress); + var r = tryCity(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -345,7 +345,7 @@ public CityResponse city(InetAddress ipAddress) throws IOException, @Override public Optional tryCity(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional response = getResponse( + var response = getResponse( ipAddress, CityResponse.class, DatabaseType.CITY, @@ -365,7 +365,7 @@ public Optional tryCity(InetAddress ipAddress) throws IOException, @Override public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryAnonymousIp(ipAddress); + var r = tryAnonymousIp(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -395,7 +395,7 @@ public Optional tryAnonymousIp(InetAddress ipAddress) throw @Override public AnonymousPlusResponse anonymousPlus(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryAnonymousPlus(ipAddress); + var r = tryAnonymousPlus(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -427,7 +427,7 @@ public Optional tryAnonymousPlus(InetAddress ipAddress) @Override public IpRiskResponse ipRisk(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryIpRisk(ipAddress); + var r = tryIpRisk(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -452,7 +452,7 @@ public Optional tryIpRisk(InetAddress ipAddress) throws IOExcept @Override public AsnResponse asn(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryAsn(ipAddress); + var r = tryAsn(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -477,7 +477,7 @@ public Optional tryAsn(InetAddress ipAddress) throws IOException, @Override public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryConnectionType(ipAddress); + var r = tryConnectionType(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -507,7 +507,7 @@ public Optional tryConnectionType(InetAddress ipAddress) @Override public DomainResponse domain(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryDomain(ipAddress); + var r = tryDomain(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -532,7 +532,7 @@ public Optional tryDomain(InetAddress ipAddress) throws IOExcept @Override public EnterpriseResponse enterprise(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryEnterprise(ipAddress); + var r = tryEnterprise(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); @@ -543,7 +543,7 @@ public EnterpriseResponse enterprise(InetAddress ipAddress) throws IOException, @Override public Optional tryEnterprise(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional response = getResponse( + var response = getResponse( ipAddress, EnterpriseResponse.class, DatabaseType.ENTERPRISE, @@ -563,7 +563,7 @@ public Optional tryEnterprise(InetAddress ipAddress) throws @Override public IspResponse isp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - Optional r = tryIsp(ipAddress); + var r = tryIsp(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException("The address " + ipAddress.getHostAddress() + " is not in the database."); diff --git a/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java b/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java index 7bc007a3d..8c61609be 100644 --- a/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java +++ b/src/main/java/com/maxmind/geoip2/InetAddressDeserializer.java @@ -21,7 +21,7 @@ public InetAddressDeserializer() { @Override public InetAddress deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - String value = p.getValueAsString(); + var value = p.getValueAsString(); if (value == null || value.isEmpty()) { return null; } diff --git a/src/main/java/com/maxmind/geoip2/NamedRecord.java b/src/main/java/com/maxmind/geoip2/NamedRecord.java index dd21b1413..b2c30ebd9 100644 --- a/src/main/java/com/maxmind/geoip2/NamedRecord.java +++ b/src/main/java/com/maxmind/geoip2/NamedRecord.java @@ -37,7 +37,7 @@ public interface NamedRecord extends JsonSerializable { */ @JsonIgnore default String name() { - for (String lang : locales()) { + for (var lang : locales()) { if (names().containsKey(lang)) { return names().get(lang); } diff --git a/src/main/java/com/maxmind/geoip2/NetworkDeserializer.java b/src/main/java/com/maxmind/geoip2/NetworkDeserializer.java index 7932b6f21..670ddc526 100644 --- a/src/main/java/com/maxmind/geoip2/NetworkDeserializer.java +++ b/src/main/java/com/maxmind/geoip2/NetworkDeserializer.java @@ -33,7 +33,7 @@ public NetworkDeserializer(Class vc) { public Network deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException { - final String cidr = jsonparser.getValueAsString(); + final var cidr = jsonparser.getValueAsString(); if (cidr == null || cidr.isBlank()) { return null; } @@ -41,13 +41,13 @@ public Network deserialize(JsonParser jsonparser, DeserializationContext context } private static Network parseCidr(String cidr) throws IOException { - final String[] parts = cidr.split("/", 2); + final var parts = cidr.split("/", 2); if (parts.length != 2) { throw new IllegalArgumentException("Invalid CIDR format: " + cidr); } - final String addrPart = parts[0]; - final String prefixPart = parts[1]; + final var addrPart = parts[0]; + final var prefixPart = parts[1]; final InetAddress address; try { @@ -56,15 +56,9 @@ private static Network parseCidr(String cidr) throws IOException { throw new IOException("Unknown host in CIDR: " + cidr, e); } - final int prefixLength; - try { - prefixLength = Integer.parseInt(prefixPart); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - "Invalid prefix length in CIDR: " + cidr, e); - } + final var prefixLength = parsePrefixLength(prefixPart, cidr); - final int maxPrefix = (address.getAddress().length == 4) ? 32 : 128; + final var maxPrefix = (address.getAddress().length == 4) ? 32 : 128; if (prefixLength < 0 || prefixLength > maxPrefix) { throw new IllegalArgumentException( "Prefix length out of range (0-" + maxPrefix + ") for CIDR: " + cidr); @@ -72,4 +66,13 @@ private static Network parseCidr(String cidr) throws IOException { return new Network(address, prefixLength); } + + private static int parsePrefixLength(String prefixPart, String cidr) { + try { + return Integer.parseInt(prefixPart); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Invalid prefix length in CIDR: " + cidr, e); + } + } } diff --git a/src/main/java/com/maxmind/geoip2/WebServiceClient.java b/src/main/java/com/maxmind/geoip2/WebServiceClient.java index 2e653201f..edad4382e 100644 --- a/src/main/java/com/maxmind/geoip2/WebServiceClient.java +++ b/src/main/java/com/maxmind/geoip2/WebServiceClient.java @@ -356,9 +356,9 @@ public InsightsResponse insights(InetAddress ipAddress) throws IOException, private T responseFor(String path, InetAddress ipAddress, Class cls) throws IOException, GeoIp2Exception { - URI uri = createUri(path, ipAddress); + var uri = createUri(path, ipAddress); - HttpRequest request = HttpRequest.newBuilder() + var request = HttpRequest.newBuilder() .uri(uri) .timeout(this.requestTimeout) .header("Accept", "application/json") @@ -366,24 +366,23 @@ private T responseFor(String path, InetAddress ipAddress, Class cls) .header("User-Agent", this.userAgent) .GET() .build(); - HttpResponse response = null; try { - response = this.httpClient + var response = this.httpClient .send(request, HttpResponse.BodyHandlers.ofInputStream()); - return handleResponse(response, cls); - } catch (InterruptedException e) { - throw new GeoIp2Exception("Interrupted sending request", e); - } finally { - if (response != null) { + try { + return handleResponse(response, cls); + } finally { response.body().close(); } + } catch (InterruptedException e) { + throw new GeoIp2Exception("Interrupted sending request", e); } } private T handleResponse(HttpResponse response, Class cls) throws GeoIp2Exception, IOException { - int status = response.statusCode(); - URI uri = response.uri(); + var status = response.statusCode(); + var uri = response.uri(); if (status >= 400 && status < 500) { this.handle4xxStatus(response); @@ -397,7 +396,7 @@ private T handleResponse(HttpResponse response, Class cls) + status + ") for " + uri, status, uri); } - InjectableValues inject = new InjectableValues.Std() + var inject = new InjectableValues.Std() .addValue("locales", locales); try { @@ -410,20 +409,17 @@ private T handleResponse(HttpResponse response, Class cls) private void handle4xxStatus(HttpResponse response) throws GeoIp2Exception, IOException { - int status = response.statusCode(); - URI uri = response.uri(); - - String body; - try (InputStream bodyStream = response.body()) { - body = new String(bodyStream.readAllBytes(), StandardCharsets.UTF_8); - if (body.equals("")) { - throw new HttpException("Received a " + status + " error for " - + uri + " with no body", status, uri); - } + var status = response.statusCode(); + var uri = response.uri(); + + final var body = readBody(response); + if (body.equals("")) { + throw new HttpException("Received a " + status + " error for " + + uri + " with no body", status, uri); } try { - Map content = mapper.readValue(body, + var content = mapper.readValue(body, new TypeReference>() { }); handleErrorWithJsonBody(content, body, status, uri); @@ -439,8 +435,8 @@ private void handle4xxStatus(HttpResponse response) private static void handleErrorWithJsonBody(Map content, String body, int status, URI uri) throws GeoIp2Exception, HttpException { - String error = content.get("error"); - String code = content.get("code"); + var error = content.get("error"); + var code = content.get("code"); if (error == null || code == null) { throw new HttpException( @@ -471,7 +467,7 @@ private static void handleErrorWithJsonBody(Map content, } private URI createUri(String service, InetAddress ipAddress) throws GeoIp2Exception { - String path = "/geoip/v2.1/" + service + "/" + var path = "/geoip/v2.1/" + service + "/" + (ipAddress == null ? "me" : ipAddress.getHostAddress()); try { return new URI( @@ -489,7 +485,7 @@ private URI createUri(String service, InetAddress ipAddress) throws GeoIp2Except } private void exhaustBody(HttpResponse response) throws HttpException { - try (InputStream body = response.body()) { + try (var body = response.body()) { // Make sure we read the stream until the end so that // the connection can be reused. while (body.read() != -1) { @@ -500,6 +496,11 @@ private void exhaustBody(HttpResponse response) throws HttpExceptio } } + private static String readBody(HttpResponse response) throws IOException { + try (var bodyStream = response.body()) { + return new String(bodyStream.readAllBytes(), StandardCharsets.UTF_8); + } + } @Override public String toString() { diff --git a/src/main/java/com/maxmind/geoip2/model/CityResponse.java b/src/main/java/com/maxmind/geoip2/model/CityResponse.java index 65a94673d..f6f35254f 100644 --- a/src/main/java/com/maxmind/geoip2/model/CityResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/CityResponse.java @@ -130,8 +130,8 @@ private static ArrayList mapSubdivisions( List subdivisions, List locales ) { - ArrayList subdivisions2 = new ArrayList<>(subdivisions.size()); - for (Subdivision subdivision : subdivisions) { + var subdivisions2 = new ArrayList(subdivisions.size()); + for (var subdivision : subdivisions) { subdivisions2.add(new Subdivision(subdivision, locales)); } return subdivisions2; diff --git a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java index 386ce6a6e..5cc60ef65 100644 --- a/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/EnterpriseResponse.java @@ -130,8 +130,8 @@ private static ArrayList mapSubdivisions( List subdivisions, List locales ) { - ArrayList subdivisions2 = new ArrayList<>(subdivisions.size()); - for (Subdivision subdivision : subdivisions) { + var subdivisions2 = new ArrayList(subdivisions.size()); + for (var subdivision : subdivisions) { subdivisions2.add(new Subdivision(subdivision, locales)); } return subdivisions2; From 0eb3d9a29f5a81bbb98ab4b411bceac8bbc62917 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 24 Oct 2025 07:11:11 -0700 Subject: [PATCH 14/17] Convert switch statement to switch expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modernize the error code handling in handleErrorWithJsonBody() to use a switch expression instead of the traditional switch statement. This makes the code more concise and aligns with the style already used in ConnectionTypeResponse. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../com/maxmind/geoip2/WebServiceClient.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/WebServiceClient.java b/src/main/java/com/maxmind/geoip2/WebServiceClient.java index edad4382e..df83018d6 100644 --- a/src/main/java/com/maxmind/geoip2/WebServiceClient.java +++ b/src/main/java/com/maxmind/geoip2/WebServiceClient.java @@ -445,22 +445,16 @@ private static void handleErrorWithJsonBody(Map content, } switch (code) { - case "IP_ADDRESS_NOT_FOUND": - case "IP_ADDRESS_RESERVED": + case "IP_ADDRESS_NOT_FOUND", "IP_ADDRESS_RESERVED" -> throw new AddressNotFoundException(error); - case "ACCOUNT_ID_REQUIRED": - case "ACCOUNT_ID_UNKNOWN": - case "AUTHORIZATION_INVALID": - case "LICENSE_KEY_REQUIRED": - case "USER_ID_REQUIRED": - case "USER_ID_UNKNOWN": + case "ACCOUNT_ID_REQUIRED", "ACCOUNT_ID_UNKNOWN", "AUTHORIZATION_INVALID", + "LICENSE_KEY_REQUIRED", "USER_ID_REQUIRED", "USER_ID_UNKNOWN" -> throw new AuthenticationException(error); - case "INSUFFICIENT_FUNDS": - case "OUT_OF_QUERIES": + case "INSUFFICIENT_FUNDS", "OUT_OF_QUERIES" -> throw new OutOfQueriesException(error); - case "PERMISSION_REQUIRED": + case "PERMISSION_REQUIRED" -> throw new PermissionRequiredException(error); - default: + default -> // These should be fairly rare throw new InvalidRequestException(error, code, uri); } From f3af96ec733d80ecad4845983ab7e0eaa13a068d Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 24 Oct 2025 07:11:33 -0700 Subject: [PATCH 15/17] Replace .equals("") with .isEmpty() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the more idiomatic String.isEmpty() method instead of comparing against an empty string literal. This expresses the intent more clearly and is the standard Java convention for empty string checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/main/java/com/maxmind/geoip2/WebServiceClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/maxmind/geoip2/WebServiceClient.java b/src/main/java/com/maxmind/geoip2/WebServiceClient.java index df83018d6..c48169a9f 100644 --- a/src/main/java/com/maxmind/geoip2/WebServiceClient.java +++ b/src/main/java/com/maxmind/geoip2/WebServiceClient.java @@ -413,7 +413,7 @@ private void handle4xxStatus(HttpResponse response) var uri = response.uri(); final var body = readBody(response); - if (body.equals("")) { + if (body.isEmpty()) { throw new HttpException("Received a " + status + " error for " + uri + " with no body", status, uri); } From 79e3d9cde462e504d14fe081a674cf424ff65e6c Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 24 Oct 2025 07:12:28 -0700 Subject: [PATCH 16/17] Use Optional.orElseThrow() pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the manual isEmpty() check and get() pattern with the more functional and concise orElseThrow() method. This eliminates 30 lines of boilerplate code across 10 lookup methods while maintaining the exact same behavior. Before: var r = tryCountry(ipAddress); if (r.isEmpty()) { throw new AddressNotFoundException(...); } return r.get(); After: return tryCountry(ipAddress).orElseThrow(() -> new AddressNotFoundException(...)); 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../com/maxmind/geoip2/DatabaseReader.java | 90 +++++++------------ 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/src/main/java/com/maxmind/geoip2/DatabaseReader.java b/src/main/java/com/maxmind/geoip2/DatabaseReader.java index cf7a7e7c2..aedfeb2ed 100644 --- a/src/main/java/com/maxmind/geoip2/DatabaseReader.java +++ b/src/main/java/com/maxmind/geoip2/DatabaseReader.java @@ -311,12 +311,9 @@ public void close() throws IOException { @Override public CountryResponse country(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryCountry(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryCountry(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -334,12 +331,9 @@ public Optional tryCountry(InetAddress ipAddress) throws IOExce @Override public CityResponse city(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryCity(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryCity(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -365,12 +359,9 @@ public Optional tryCity(InetAddress ipAddress) throws IOException, @Override public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryAnonymousIp(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryAnonymousIp(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -395,12 +386,9 @@ public Optional tryAnonymousIp(InetAddress ipAddress) throw @Override public AnonymousPlusResponse anonymousPlus(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryAnonymousPlus(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryAnonymousPlus(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -427,12 +415,9 @@ public Optional tryAnonymousPlus(InetAddress ipAddress) @Override public IpRiskResponse ipRisk(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryIpRisk(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryIpRisk(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -452,12 +437,9 @@ public Optional tryIpRisk(InetAddress ipAddress) throws IOExcept @Override public AsnResponse asn(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryAsn(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryAsn(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -477,12 +459,9 @@ public Optional tryAsn(InetAddress ipAddress) throws IOException, @Override public ConnectionTypeResponse connectionType(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryConnectionType(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryConnectionType(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -507,12 +486,9 @@ public Optional tryConnectionType(InetAddress ipAddress) @Override public DomainResponse domain(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryDomain(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryDomain(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -532,12 +508,9 @@ public Optional tryDomain(InetAddress ipAddress) throws IOExcept @Override public EnterpriseResponse enterprise(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryEnterprise(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryEnterprise(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override @@ -563,12 +536,9 @@ public Optional tryEnterprise(InetAddress ipAddress) throws @Override public IspResponse isp(InetAddress ipAddress) throws IOException, GeoIp2Exception { - var r = tryIsp(ipAddress); - if (r.isEmpty()) { - throw new AddressNotFoundException("The address " - + ipAddress.getHostAddress() + " is not in the database."); - } - return r.get(); + return tryIsp(ipAddress).orElseThrow(() -> + new AddressNotFoundException("The address " + + ipAddress.getHostAddress() + " is not in the database.")); } @Override From 521818939de3cb9e9f1ed390bfe8df8d92c2c4ac Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Fri, 24 Oct 2025 07:13:05 -0700 Subject: [PATCH 17/17] Seal GeoIp2Exception hierarchy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make GeoIp2Exception a sealed class with an explicit permits clause listing all allowed subclasses. This provides: - Explicit API stability guarantees - new exception types cannot be added externally - Compiler-enforced exhaustiveness checking for pattern matching in future Java versions - Clear documentation of the complete exception hierarchy All subclasses (AddressNotFoundException, AuthenticationException, InvalidRequestException, OutOfQueriesException, and PermissionRequiredException) were already marked as final, making them compatible with the sealed parent. Note: HttpException extends IOException, not GeoIp2Exception, so it remains outside this sealed hierarchy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../java/com/maxmind/geoip2/exception/GeoIp2Exception.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/maxmind/geoip2/exception/GeoIp2Exception.java b/src/main/java/com/maxmind/geoip2/exception/GeoIp2Exception.java index 967cd034a..24afd18df 100644 --- a/src/main/java/com/maxmind/geoip2/exception/GeoIp2Exception.java +++ b/src/main/java/com/maxmind/geoip2/exception/GeoIp2Exception.java @@ -4,7 +4,12 @@ * This class represents a generic GeoIP2 error. All other exceptions thrown by * the GeoIP2 API subclass this exception */ -public class GeoIp2Exception extends Exception { +public sealed class GeoIp2Exception extends Exception + permits AddressNotFoundException, + AuthenticationException, + InvalidRequestException, + OutOfQueriesException, + PermissionRequiredException { /**