Skip to content
Snippets Groups Projects
Commit 9b0dbeec authored by Martin Lowe's avatar Martin Lowe :flag_ca:
Browse files

Merge branch 'malowe/master/commons-1-q-3.8' into 'master'

Upgrade API to use commons SDK 1.0 RC and Quarkus 3.8.x

See merge request !47
parents fee31f27 3f706b29
No related branches found
No related tags found
1 merge request!47Upgrade API to use commons SDK 1.0 RC and Quarkus 3.8.x
Pipeline #45178 passed
...@@ -9,7 +9,6 @@ dev-start:; ...@@ -9,7 +9,6 @@ dev-start:;
mvn compile quarkus:dev mvn compile quarkus:dev
clean:; clean:;
rm -rf ./maxmind
mvn clean mvn clean
compile-test-resources: install-yarn; compile-test-resources: install-yarn;
......
...@@ -15,10 +15,10 @@ ...@@ -15,10 +15,10 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.2.11.Final</quarkus.platform.version> <quarkus.platform.version>3.8.3</quarkus.platform.version>
<surefire-plugin.version>3.1.2</surefire-plugin.version> <surefire-plugin.version>3.1.2</surefire-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters> <maven.compiler.parameters>true</maven.compiler.parameters>
<eclipse-api-version>0.9.5</eclipse-api-version> <eclipse-api-version>1.0.0-RC1</eclipse-api-version>
<auto-value.version>1.10.4</auto-value.version> <auto-value.version>1.10.4</auto-value.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version> <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<sonar.sources>src/main</sonar.sources> <sonar.sources>src/main</sonar.sources>
......
...@@ -14,17 +14,17 @@ package org.eclipsefoundation.geoip.client.resources; ...@@ -14,17 +14,17 @@ package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.service.GeoIPService; import org.eclipsefoundation.geoip.client.service.GeoIPService;
import org.eclipsefoundation.http.exception.ApplicationException;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import com.maxmind.geoip2.record.AbstractNamedRecord; import com.maxmind.geoip2.record.AbstractNamedRecord;
import jakarta.inject.Inject;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.core.Response;
/** /**
* Superclass of {@link CountryResource} and {@link CityResource}. Contains a * Superclass of {@link CountryResource} and {@link CityResource}. Contains a
* method for converting a City/Country to Json, as well as validation for * method for converting a City/Country to Json, as well as validation for
......
...@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response; ...@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response;
@Path("/cities") @Path("/cities")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public class CityResource extends AbstractLocationResource { public class CityResource extends AbstractLocationResource {
/** /**
* Returns a 200 OK Response containing the city location of a given IP address. * Returns a 200 OK Response containing the city location of a given IP address.
* Returns a 400 Bad Request if the IP is invalid. Returns a 500 Internal Server * Returns a 400 Bad Request if the IP is invalid. Returns a 500 Internal Server
......
...@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response; ...@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response;
@Path("/countries") @Path("/countries")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public class CountryResource extends AbstractLocationResource { public class CountryResource extends AbstractLocationResource {
/** /**
* Returns a 200 OK Response containing the country location of a given IP * Returns a 200 OK Response containing the country location of a given IP
* address. Returns a 400 Bad Request if the IP is invalid. Returns a 500 * address. Returns a 400 Bad Request if the IP is invalid. Returns a 500
......
...@@ -21,20 +21,18 @@ import java.util.List; ...@@ -21,20 +21,18 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.config.EclipseMaxmindSubnetConfig; import org.eclipsefoundation.geoip.client.config.EclipseMaxmindSubnetConfig;
import org.eclipsefoundation.geoip.client.model.Country; import org.eclipsefoundation.geoip.client.model.Country;
import org.eclipsefoundation.geoip.client.model.IPVersion; import org.eclipsefoundation.geoip.client.model.IPVersion;
import org.eclipsefoundation.geoip.client.model.SubnetRange; import org.eclipsefoundation.geoip.client.model.SubnetRange;
import org.eclipsefoundation.geoip.client.service.NetworkService; import org.eclipsefoundation.geoip.client.service.NetworkService;
import org.eclipsefoundation.http.exception.ApplicationException;
import com.opencsv.bean.CsvToBeanBuilder; import com.opencsv.bean.CsvToBeanBuilder;
import io.quarkus.runtime.Startup; import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
/** /**
* Loads Network subnet information via CSV files on the system. * Loads Network subnet information via CSV files on the system.
...@@ -46,86 +44,84 @@ import io.quarkus.runtime.Startup; ...@@ -46,86 +44,84 @@ import io.quarkus.runtime.Startup;
@ApplicationScoped @ApplicationScoped
public class CSVNetworkService implements NetworkService { public class CSVNetworkService implements NetworkService {
@Inject private EclipseMaxmindSubnetConfig config;
EclipseMaxmindSubnetConfig config;
public CSVNetworkService(EclipseMaxmindSubnetConfig config) {
this.config = config;
}
private Map<String, String> countryIdToIso; private Map<String, String> countryIdToIso;
private Map<String, List<String>> ipv4Subnets; private Map<String, List<String>> ipv4Subnets;
private Map<String, List<String>> ipv6Subnets; private Map<String, List<String>> ipv6Subnets;
@PostConstruct @PostConstruct
public void init() { public void init() {
this.ipv4Subnets = new HashMap<>(); this.ipv4Subnets = new HashMap<>();
this.ipv6Subnets = new HashMap<>(); this.ipv6Subnets = new HashMap<>();
this.countryIdToIso = new HashMap<>(); this.countryIdToIso = new HashMap<>();
loadCountries(config.countriesFilePath(), countryIdToIso); loadCountries(config.countriesFilePath(), countryIdToIso);
loadMap(config.ipv4FilePath(), ipv4Subnets); loadMap(config.ipv4FilePath(), ipv4Subnets);
// Pre-loading ipv6 entries is a 2million item list and crashes the app // Pre-loading ipv6 entries is a 2million item list and crashes the app
} }
@Override @Override
public List<String> getSubnets(String countryCode, IPVersion ipv) { public List<String> getSubnets(String countryCode, IPVersion ipv) {
Objects.requireNonNull(countryCode); Objects.requireNonNull(countryCode);
// match on ipversion, and check that we have value. If found, return copy, else // match on ipversion, and check that we have value. If found, return copy, else
// return empty list // return empty list
if (IPVersion.IPV4.equals(ipv) && ipv4Subnets.containsKey(countryCode)) { if (IPVersion.IPV4.equals(ipv) && ipv4Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv4Subnets.get(countryCode.toLowerCase())); return new ArrayList<>(ipv4Subnets.get(countryCode.toLowerCase()));
} else if (IPVersion.IPV6.equals(ipv) && ipv6Subnets.containsKey(countryCode)) { } else if (IPVersion.IPV6.equals(ipv) && ipv6Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv6Subnets.get(countryCode.toLowerCase())); return new ArrayList<>(ipv6Subnets.get(countryCode.toLowerCase()));
} }
return Collections.emptyList(); return Collections.emptyList();
} }
/** /**
* Reads all Country entities from a desired CSV file and loads them into the * Reads all Country entities from a desired CSV file and loads them into the desired container.
* desired container. *
* * @param filePath The location of the CSV file to read.
* @param filePath The location of the CSV file to read. * @param container Reference to the Map to store all entries read from the file.
* @param container Reference to the Map to store all entries read from the */
* file. private void loadCountries(String filePath, Map<String, String> container) {
*/ try (FileReader reader = new FileReader(filePath)) {
private void loadCountries(String filePath, Map<String, String> container) { // read in all of the country lines as country objects
try (FileReader reader = new FileReader(filePath)) { List<Country> countries = new CsvToBeanBuilder<Country>(reader).withType(Country.class).build().parse();
// read in all of the country lines as country objects // add each of the countries to the container map, mapping ID to the ISO code
List<Country> countries = new CsvToBeanBuilder<Country>(reader).withType(Country.class).build().parse(); for (Country c : countries) {
// add each of the countries to the container map, mapping ID to the ISO code container.put(c.getId(), c.getCountryIsoCode().toLowerCase());
for (Country c : countries) { }
container.put(c.getId(), c.getCountryIsoCode().toLowerCase()); } catch (FileNotFoundException e) {
} throw new ApplicationException("Could not find country CSV file at path " + filePath, e);
} catch (FileNotFoundException e) { } catch (IOException e) {
throw new ApplicationException("Could not find country CSV file at path " + filePath, e); throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
} catch (IOException e) { }
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e); }
}
}
/** /**
* Reads all Subnet entities from a desired CSV file and loads them into the * Reads all Subnet entities from a desired CSV file and loads them into the desired container.
* desired container. *
* * @param filePath The location of the CSV file to read.
* @param filePath The location of the CSV file to read. * @param container Reference to the Map to store all entries read from the file.
* @param container Reference to the Map to store all entries read from the */
* file. private void loadMap(String filePath, Map<String, List<String>> container) {
*/ try (FileReader reader = new FileReader(filePath)) {
private void loadMap(String filePath, Map<String, List<String>> container) { // read in all of the country lines as country objects
try (FileReader reader = new FileReader(filePath)) { List<SubnetRange> subnets = new CsvToBeanBuilder<SubnetRange>(reader).withType(SubnetRange.class).build().parse();
// read in all of the country lines as country objects for (SubnetRange subnet : subnets) {
List<SubnetRange> subnets = new CsvToBeanBuilder<SubnetRange>(reader).withType(SubnetRange.class).build() // for each of the subnet ranges, get the best available ID
.parse(); String actualId = subnet.getGeoname() != null ? subnet.getGeoname() : subnet.getRegisteredGeoname();
for (SubnetRange subnet : subnets) { // lookup the ISO code for the current entry
// for each of the subnet ranges, get the best available ID String isoCode = countryIdToIso.get(actualId);
String actualId = subnet.getGeoname() != null ? subnet.getGeoname() : subnet.getRegisteredGeoname(); // if exists, add to subnet range list in map
// lookup the ISO code for the current entry if (isoCode != null) {
String isoCode = countryIdToIso.get(actualId); container.computeIfAbsent(isoCode, k -> new ArrayList<>()).add(subnet.getNetwork());
// if exists, add to subnet range list in map }
if (isoCode != null) { }
container.computeIfAbsent(isoCode, k -> new ArrayList<>()).add(subnet.getNetwork()); } catch (FileNotFoundException e) {
} throw new ApplicationException("Could not find country CSV file at path " + filePath, e);
} } catch (IOException e) {
} catch (FileNotFoundException e) { throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
throw new ApplicationException("Could not find country CSV file at path " + filePath, e); }
} catch (IOException e) { }
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
}
}
} }
...@@ -16,14 +16,9 @@ import java.io.File; ...@@ -16,14 +16,9 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.config.MaxmindDatabaseConfig; import org.eclipsefoundation.geoip.client.config.MaxmindDatabaseConfig;
import org.eclipsefoundation.geoip.client.service.GeoIPService; import org.eclipsefoundation.geoip.client.service.GeoIPService;
import org.eclipsefoundation.http.exception.ApplicationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -34,71 +29,75 @@ import com.maxmind.geoip2.record.City; ...@@ -34,71 +29,75 @@ import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country; import com.maxmind.geoip2.record.Country;
import io.quarkus.runtime.Startup; import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
/** /**
* Implementation of the Geo-IP service using MaxMind. Built in caching is * Implementation of the Geo-IP service using MaxMind. Built in caching is ignored as its a very light implementation that has no eviction
* ignored as its a very light implementation that has no eviction policy and * policy and silently stops using cache if it gets full.
* silently stops using cache if it gets full.
* *
* @author Martin Lowe * @author Martin Lowe
*/ */
@Startup @Startup
@ApplicationScoped @ApplicationScoped
public class MaxMindGeoIPService implements GeoIPService { public class MaxMindGeoIPService implements GeoIPService {
private static final Logger LOGGER = LoggerFactory.getLogger(MaxMindGeoIPService.class); private static final Logger LOGGER = LoggerFactory.getLogger(MaxMindGeoIPService.class);
@Inject private MaxmindDatabaseConfig config;
MaxmindDatabaseConfig config; private DatabaseReader countryReader;
private DatabaseReader cityReader;
private DatabaseReader countryReader; public MaxMindGeoIPService(MaxmindDatabaseConfig config) {
private DatabaseReader cityReader; this.config = config;
}
/** /**
* Create instances of DB readers for each available DB * Create instances of DB readers for each available DB
*/ */
@PostConstruct @PostConstruct
public void init() { public void init() {
File countryDb = new File(config.dbRoot() + File.separator + config.countryFileName()); File countryDb = new File(config.dbRoot() + File.separator + config.countryFileName());
File cityDb = new File(config.dbRoot() + File.separator + config.cityFileName()); File cityDb = new File(config.dbRoot() + File.separator + config.cityFileName());
// attempt to load db's, throwing up if there is an issue initializing the // attempt to load db's, throwing up if there is an issue initializing the
// readers // readers
try { try {
this.countryReader = new DatabaseReader.Builder(countryDb).build(); this.countryReader = new DatabaseReader.Builder(countryDb).build();
this.cityReader = new DatabaseReader.Builder(cityDb).build(); this.cityReader = new DatabaseReader.Builder(cityDb).build();
} catch (IOException e) { } catch (IOException e) {
throw new ApplicationException("Error pre-loading Maxmind DatabaseReaders", e); throw new ApplicationException("Error pre-loading Maxmind DatabaseReaders", e);
} }
} }
@PreDestroy @PreDestroy
public void destroy() { public void destroy() {
try { try {
this.countryReader.close(); this.countryReader.close();
this.cityReader.close(); this.cityReader.close();
} catch (IOException e) { } catch (IOException e) {
LOGGER.error("Error while closing Geo IP databases, potential memory leak", e); LOGGER.error("Error while closing Geo IP databases, potential memory leak", e);
} }
} }
@Override @Override
public City getCity(String ipAddr) { public City getCity(String ipAddr) {
try { try {
InetAddress addr = InetAddress.getByName(ipAddr); InetAddress addr = InetAddress.getByName(ipAddr);
CityResponse resp = cityReader.city(addr); CityResponse resp = cityReader.city(addr);
return resp.getCity(); return resp.getCity();
} catch (Exception e) { } catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e); throw new ApplicationException("Error interacting with GeoIP databases", e);
} }
} }
@Override @Override
public Country getCountry(String ipAddr) { public Country getCountry(String ipAddr) {
try { try {
InetAddress addr = InetAddress.getByName(ipAddr); InetAddress addr = InetAddress.getByName(ipAddr);
CountryResponse resp = countryReader.country(addr); CountryResponse resp = countryReader.country(addr);
return resp.getCountry(); return resp.getCountry();
} catch (Exception e) { } catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e); throw new ApplicationException("Error interacting with GeoIP databases", e);
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment