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:;
mvn compile quarkus:dev
clean:;
rm -rf ./maxmind
mvn clean
compile-test-resources: install-yarn;
......
......@@ -15,10 +15,10 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-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>
<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>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
<sonar.sources>src/main</sonar.sources>
......
......@@ -14,17 +14,17 @@ package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException;
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.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
import org.eclipsefoundation.http.exception.ApplicationException;
import com.google.common.net.InetAddresses;
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
* method for converting a City/Country to Json, as well as validation for
......
......@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response;
@Path("/cities")
@Produces(MediaType.APPLICATION_JSON)
public class CityResource extends AbstractLocationResource {
/**
* 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
......
......@@ -26,7 +26,7 @@ import jakarta.ws.rs.core.Response;
@Path("/countries")
@Produces(MediaType.APPLICATION_JSON)
public class CountryResource extends AbstractLocationResource {
/**
* 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
......
......@@ -21,20 +21,18 @@ import java.util.List;
import java.util.Map;
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.model.Country;
import org.eclipsefoundation.geoip.client.model.IPVersion;
import org.eclipsefoundation.geoip.client.model.SubnetRange;
import org.eclipsefoundation.geoip.client.service.NetworkService;
import org.eclipsefoundation.http.exception.ApplicationException;
import com.opencsv.bean.CsvToBeanBuilder;
import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
/**
* Loads Network subnet information via CSV files on the system.
......@@ -46,86 +44,84 @@ import io.quarkus.runtime.Startup;
@ApplicationScoped
public class CSVNetworkService implements NetworkService {
@Inject
EclipseMaxmindSubnetConfig config;
private EclipseMaxmindSubnetConfig config;
public CSVNetworkService(EclipseMaxmindSubnetConfig config) {
this.config = config;
}
private Map<String, String> countryIdToIso;
private Map<String, List<String>> ipv4Subnets;
private Map<String, List<String>> ipv6Subnets;
private Map<String, String> countryIdToIso;
private Map<String, List<String>> ipv4Subnets;
private Map<String, List<String>> ipv6Subnets;
@PostConstruct
public void init() {
this.ipv4Subnets = new HashMap<>();
this.ipv6Subnets = new HashMap<>();
this.countryIdToIso = new HashMap<>();
loadCountries(config.countriesFilePath(), countryIdToIso);
loadMap(config.ipv4FilePath(), ipv4Subnets);
// Pre-loading ipv6 entries is a 2million item list and crashes the app
}
@PostConstruct
public void init() {
this.ipv4Subnets = new HashMap<>();
this.ipv6Subnets = new HashMap<>();
this.countryIdToIso = new HashMap<>();
loadCountries(config.countriesFilePath(), countryIdToIso);
loadMap(config.ipv4FilePath(), ipv4Subnets);
// Pre-loading ipv6 entries is a 2million item list and crashes the app
}
@Override
public List<String> getSubnets(String countryCode, IPVersion ipv) {
Objects.requireNonNull(countryCode);
// match on ipversion, and check that we have value. If found, return copy, else
// return empty list
if (IPVersion.IPV4.equals(ipv) && ipv4Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv4Subnets.get(countryCode.toLowerCase()));
} else if (IPVersion.IPV6.equals(ipv) && ipv6Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv6Subnets.get(countryCode.toLowerCase()));
}
return Collections.emptyList();
}
@Override
public List<String> getSubnets(String countryCode, IPVersion ipv) {
Objects.requireNonNull(countryCode);
// match on ipversion, and check that we have value. If found, return copy, else
// return empty list
if (IPVersion.IPV4.equals(ipv) && ipv4Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv4Subnets.get(countryCode.toLowerCase()));
} else if (IPVersion.IPV6.equals(ipv) && ipv6Subnets.containsKey(countryCode)) {
return new ArrayList<>(ipv6Subnets.get(countryCode.toLowerCase()));
}
return Collections.emptyList();
}
/**
* Reads all Country entities from a desired CSV file and loads them into the
* desired container.
*
* @param filePath The location of the CSV file to read.
* @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)) {
// read in all of the country lines as country objects
List<Country> countries = new CsvToBeanBuilder<Country>(reader).withType(Country.class).build().parse();
// add each of the countries to the container map, mapping ID to the ISO code
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 (IOException e) {
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
}
}
/**
* Reads all Country entities from a desired CSV file and loads them into the desired container.
*
* @param filePath The location of the CSV file to read.
* @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)) {
// read in all of the country lines as country objects
List<Country> countries = new CsvToBeanBuilder<Country>(reader).withType(Country.class).build().parse();
// add each of the countries to the container map, mapping ID to the ISO code
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 (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
* desired container.
*
* @param filePath The location of the CSV file to read.
* @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)) {
// read in all of the country lines as country objects
List<SubnetRange> subnets = new CsvToBeanBuilder<SubnetRange>(reader).withType(SubnetRange.class).build()
.parse();
for (SubnetRange subnet : subnets) {
// for each of the subnet ranges, get the best available ID
String actualId = subnet.getGeoname() != null ? subnet.getGeoname() : subnet.getRegisteredGeoname();
// lookup the ISO code for the current entry
String isoCode = countryIdToIso.get(actualId);
// 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) {
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 desired container.
*
* @param filePath The location of the CSV file to read.
* @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)) {
// read in all of the country lines as country objects
List<SubnetRange> subnets = new CsvToBeanBuilder<SubnetRange>(reader).withType(SubnetRange.class).build().parse();
for (SubnetRange subnet : subnets) {
// for each of the subnet ranges, get the best available ID
String actualId = subnet.getGeoname() != null ? subnet.getGeoname() : subnet.getRegisteredGeoname();
// lookup the ISO code for the current entry
String isoCode = countryIdToIso.get(actualId);
// 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) {
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
}
}
}
......@@ -16,14 +16,9 @@ import java.io.File;
import java.io.IOException;
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.service.GeoIPService;
import org.eclipsefoundation.http.exception.ApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -34,71 +29,75 @@ import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country;
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
* ignored as its a very light implementation that has no eviction policy and
* silently stops using cache if it gets full.
* Implementation of the Geo-IP service using MaxMind. Built in caching is ignored as its a very light implementation that has no eviction
* policy and silently stops using cache if it gets full.
*
* @author Martin Lowe
*/
@Startup
@ApplicationScoped
public class MaxMindGeoIPService implements GeoIPService {
private static final Logger LOGGER = LoggerFactory.getLogger(MaxMindGeoIPService.class);
private static final Logger LOGGER = LoggerFactory.getLogger(MaxMindGeoIPService.class);
@Inject
MaxmindDatabaseConfig config;
private MaxmindDatabaseConfig config;
private DatabaseReader countryReader;
private DatabaseReader cityReader;
private DatabaseReader countryReader;
private DatabaseReader cityReader;
public MaxMindGeoIPService(MaxmindDatabaseConfig config) {
this.config = config;
}
/**
* Create instances of DB readers for each available DB
*/
@PostConstruct
public void init() {
File countryDb = new File(config.dbRoot() + File.separator + config.countryFileName());
File cityDb = new File(config.dbRoot() + File.separator + config.cityFileName());
// attempt to load db's, throwing up if there is an issue initializing the
// readers
try {
this.countryReader = new DatabaseReader.Builder(countryDb).build();
this.cityReader = new DatabaseReader.Builder(cityDb).build();
} catch (IOException e) {
throw new ApplicationException("Error pre-loading Maxmind DatabaseReaders", e);
}
}
/**
* Create instances of DB readers for each available DB
*/
@PostConstruct
public void init() {
File countryDb = new File(config.dbRoot() + File.separator + config.countryFileName());
File cityDb = new File(config.dbRoot() + File.separator + config.cityFileName());
// attempt to load db's, throwing up if there is an issue initializing the
// readers
try {
this.countryReader = new DatabaseReader.Builder(countryDb).build();
this.cityReader = new DatabaseReader.Builder(cityDb).build();
} catch (IOException e) {
throw new ApplicationException("Error pre-loading Maxmind DatabaseReaders", e);
}
}
@PreDestroy
public void destroy() {
try {
this.countryReader.close();
this.cityReader.close();
} catch (IOException e) {
LOGGER.error("Error while closing Geo IP databases, potential memory leak", e);
}
}
@PreDestroy
public void destroy() {
try {
this.countryReader.close();
this.cityReader.close();
} catch (IOException e) {
LOGGER.error("Error while closing Geo IP databases, potential memory leak", e);
}
}
@Override
public City getCity(String ipAddr) {
try {
InetAddress addr = InetAddress.getByName(ipAddr);
CityResponse resp = cityReader.city(addr);
return resp.getCity();
} catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e);
}
}
@Override
public City getCity(String ipAddr) {
try {
InetAddress addr = InetAddress.getByName(ipAddr);
CityResponse resp = cityReader.city(addr);
return resp.getCity();
} catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e);
}
}
@Override
public Country getCountry(String ipAddr) {
try {
InetAddress addr = InetAddress.getByName(ipAddr);
CountryResponse resp = countryReader.country(addr);
return resp.getCountry();
} catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e);
}
}
@Override
public Country getCountry(String ipAddr) {
try {
InetAddress addr = InetAddress.getByName(ipAddr);
CountryResponse resp = countryReader.country(addr);
return resp.getCountry();
} catch (Exception 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