com.github.sypexgeo.SxRestClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sypexgeo Show documentation
Show all versions of sypexgeo Show documentation
Java API for Sypex Geo Web Service
The newest version!
package com.github.sypexgeo;
import com.github.sypexgeo.model.SxCity;
import com.github.sypexgeo.model.SxCoordinates;
import com.github.sypexgeo.model.SxCountry;
import com.github.sypexgeo.model.SxGeoResult;
import com.github.sypexgeo.model.SxId;
import com.github.sypexgeo.model.SxName;
import com.github.sypexgeo.model.SxRegion;
import com.github.sypexgeo.model.SxValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;
/**
* Main class of the package: executes requests for Sypex server.
*/
public class SxRestClient {
private static final Pattern IPV4_PATTERN = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
private static final Pattern IPV4_COMMA_SEPARATED_PATTERN =
Pattern.compile("((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)(,\\n|,?$))");
@Nullable
private final String key;
@Nullable
protected SxCache cache;
protected int clientQueriesCount;
protected int restQueriesCount;
protected SxRestClient(@Nullable String key) {
this.key = key;
}
/**
* Creates new SxRestClient initialized with unique customer key.
* If customer key is null - anonymous queries are used (<=10k per month are allowed).
*/
@NotNull
public static SxRestClient create(@Nullable String key) {
return new SxRestClient(key);
}
/**
* Executes REST request and return results for a given IPs.
* Normally there is only 1 result in the list per IP.
*
* @param ip IP to get geo info for. Multiple IPs can be used with comma as separator.
* @return list of SxGeoResult results
* @throws IllegalArgumentException if IP address is invalid.
*/
@NotNull
public List getList(@NotNull String ip) {
if (!IPV4_COMMA_SEPARATED_PATTERN.matcher(ip).matches()) {
throw new IllegalArgumentException("Illegal IP address or list: " + ip);
}
clientQueriesCount++;
List cachedResult = cache == null ? null : cache.getList(ip);
if (cachedResult != null) {
return cachedResult;
}
try {
NodeList ipNodes = query(ip);
ArrayList result = new ArrayList<>();
for (int i = 0; i < ipNodes.getLength(); i++) {
result.add(parseIp((Element) ipNodes.item(i)));
}
if (cache != null) {
cache.add(ip, result);
}
return result;
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new RuntimeException(e);
}
}
/**
* Executes REST request for a single IP address.
*/
@Nullable
public SxGeoResult get(@NotNull String ip) {
if (!IPV4_PATTERN.matcher(ip).matches()) {
throw new IllegalArgumentException("Illegal IP address: " + ip);
}
clientQueriesCount++;
List cachedResult = cache == null ? null : cache.getList(ip);
if (cachedResult != null) {
return cachedResult.isEmpty() ? null : cachedResult.get(0);
}
try {
NodeList ipNodes = query(ip);
SxGeoResult result = ipNodes.getLength() == 0 ? null : parseIp((Element) ipNodes.item(0));
if (cache != null) {
cache.add(ip, Collections.singletonList(result));
}
return result;
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new RuntimeException(e);
}
}
/**
* Sets cache to be used before making any network call or null if no cache is needed.
* By default no cache is used.
*
* @param cache to be used before calling remote service. If null -> uses default mode without caching.
*/
public void setCache(@Nullable SxCache cache) {
this.cache = cache;
}
private NodeList query(@NotNull String ip) throws ParserConfigurationException, SAXException, IOException {
restQueriesCount++;
URL url = new URL("http://api.sypexgeo.net/" + (key == null ? "" : key + "/") + "xml/" + ip);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
connection.setRequestProperty("User-Agent", "SypexGeo4J Java client, https://github.com/sypexgeo/sypexgeo4j");
try (InputStream is = connection.getInputStream()) {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(is);
return doc.getElementsByTagName("ip");
}
}
private static SxGeoResult parseIp(Element ipNode) {
NodeList cityNodes = ipNode.getElementsByTagName("city");
SxCity city = cityNodes.getLength() == 0 ? null : parseCity((Element) cityNodes.item(0));
NodeList regionNodes = ipNode.getElementsByTagName("region");
SxRegion region = regionNodes.getLength() == 0 ? null : parseRegion((Element) regionNodes.item(0));
NodeList countryNodes = ipNode.getElementsByTagName("country");
SxCountry country = countryNodes.getLength() == 0 ? null : parseCountry((Element) countryNodes.item(0));
Map ipAttributes = new HashMap<>();
NamedNodeMap attributesMap = ipNode.getAttributes();
for (int i = 0; i < attributesMap.getLength(); i++) {
Node attribute = attributesMap.item(i);
ipAttributes.put(attribute.getNodeName(), attribute.getNodeValue());
}
return new SxGeoResult(ipAttributes, city, region, country);
}
@Nullable
private static SxCity parseCity(@NotNull Element city) {
SxId id = getId(city);
return id == null ? null : new SxCity(id, getCoordinates(city), getName(city), getTimeZone(city), getAttributes(city));
}
@Nullable
private static SxRegion parseRegion(@NotNull Element region) {
SxId id = getId(region);
return id == null ? null : new SxRegion(id, getCoordinates(region), getName(region), getTimeZone(region), getAttributes(region));
}
@Nullable
private static SxCountry parseCountry(Element country) {
SxId id = getId(country);
return id == null ? null : new SxCountry(id, getCoordinates(country), getName(country), getTimeZone(country), getAttributes(country));
}
@NotNull
private static String getRequiredValue(Element e, String tag) {
return e.getElementsByTagName(tag).item(0).getTextContent();
}
@Nullable
private static String getValue(Element e, String tag) {
NodeList nodeList = e.getElementsByTagName(tag);
return nodeList.getLength() > 0 ? nodeList.item(0).getTextContent() : null;
}
@Nullable
private static SxId getId(Element e) {
String value = getValue(e, SxValue.ID);
return value == null || value.isEmpty() ? null : new SxId(Integer.parseInt(value));
}
@NotNull
private static SxCoordinates getCoordinates(Element e) {
String lat = getRequiredValue(e, SxValue.LAT);
String lon = getRequiredValue(e, SxValue.LON);
return new SxCoordinates(Double.parseDouble(lat), Double.parseDouble(lon));
}
@NotNull
private static SxName getName(Element e) {
Map valuesByCode = new HashMap<>();
for (Element el : getChildrenByPrefix(e, "name_")) {
String code = el.getTagName().substring(5);
String val = el.getTextContent();
valuesByCode.put(code, val);
}
return new SxName(valuesByCode);
}
@Nullable
private static TimeZone getTimeZone(Element e) {
String timezoneId = getValue(e, SxValue.TIMEZONE);
return timezoneId != null ? TimeZone.getTimeZone(timezoneId) : null;
}
@NotNull
private static List getChildrenByPrefix(@NotNull Element e, @NotNull String prefix) {
List res = new ArrayList<>();
NodeList nodes = e.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element && ((Element) node).getTagName().startsWith(prefix)) {
res.add((Element) node);
}
}
return res;
}
@NotNull
private static Map getAttributes(Element element) {
Map result = new HashMap<>();
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element e = (Element) node;
result.put(e.getTagName(), new SxValue(e.getTextContent()));
}
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy