com.maxmind.geoip2.DatabaseReader Maven / Gradle / Ivy
package com.maxmind.geoip2;
import com.maxmind.db.*;
import com.maxmind.db.Reader.FileMode;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Modified and simplified for I2P
*
*
* The class {@code DatabaseReader} provides a reader for the GeoIP2 database
* format.
*
* Usage
*
* To use the database API, you must create a new {@code DatabaseReader} using
* the {@code DatabaseReader.Builder}. You must provide the {@code Builder}
* constructor either an {@code InputStream} or {@code File} for your GeoIP2
* database. You may also specify the {@code fileMode} and the {@code locales}
* fallback order using the methods on the {@code Builder} object. After you
* have created the {@code DatabaseReader}, you may then call the appropriate
* method (e.g., {@code city}) for your database, passing it the IP address
* you want to look up.
*
*
* If the lookup succeeds, the method call will return a response class for
* the GeoIP2 lookup. The class in turn contains multiple record classes,
* each of which represents part of the data returned by the database.
*
*
* We recommend reusing the {@code DatabaseReader} object rather than creating
* a new one for each lookup. The creation of this object is relatively
* expensive as it must read in metadata for the file. It is safe to share the
* object across threads.
*
* Caching
*
* The database API supports pluggable caching (by default, no caching is
* performed). A simple implementation is provided by
* {@code com.maxmind.db.CHMCache}. Using this cache, lookup performance is
* significantly improved at the cost of a small (~2MB) memory overhead.
*
*
* @since 0.9.38
*/
public class DatabaseReader implements Closeable {
private final Reader reader;
private final List locales;
private DatabaseReader(Builder builder) throws IOException {
if (builder.stream != null) {
this.reader = new Reader(builder.stream, builder.cache);
} else if (builder.database != null) {
this.reader = new Reader(builder.database, builder.mode, builder.cache);
} else {
// This should never happen. If it does, review the Builder class
// constructors for errors.
throw new IllegalArgumentException(
"Unsupported Builder configuration: expected either File or URL");
}
this.locales = builder.locales;
}
/**
*
* Constructs a Builder for the {@code DatabaseReader}. The file passed to
* it must be a valid GeoIP2 database file.
*
*
* {@code Builder} creates instances of {@code DatabaseReader}
* from values set by the methods.
*
*
* Only the values set in the {@code Builder} constructor are required.
*
*/
public static final class Builder {
final File database;
final InputStream stream;
List locales = Collections.singletonList("en");
FileMode mode = FileMode.MEMORY_MAPPED;
NodeCache cache = NoCache.getInstance();
/**
* @param stream the stream containing the GeoIP2 database to use.
*/
public Builder(InputStream stream) {
this.stream = stream;
this.database = null;
}
/**
* @param database the GeoIP2 database file to use.
*/
public Builder(File database) {
this.database = database;
this.stream = null;
}
/**
* @param val List of locale codes to use in name property from most
* preferred to least preferred.
* @return Builder object
*/
public Builder locales(List val) {
this.locales = val;
return this;
}
/**
* @param cache backing cache instance
* @return Builder object
*/
public Builder withCache(NodeCache cache) {
this.cache = cache;
return this;
}
/**
* @param val The file mode used to open the GeoIP2 database
* @return Builder object
* @throws java.lang.IllegalArgumentException if you initialized the Builder with a URL, which uses
* {@link FileMode#MEMORY}, but you provided a different
* FileMode to this method.
*/
public Builder fileMode(FileMode val) {
if (this.stream != null && FileMode.MEMORY != val) {
throw new IllegalArgumentException(
"Only FileMode.MEMORY is supported when using an InputStream.");
}
this.mode = val;
return this;
}
/**
* @return an instance of {@code DatabaseReader} created from the
* fields set on this builder.
* @throws IOException if there is an error reading the database
*/
public DatabaseReader build() throws IOException {
return new DatabaseReader(this);
}
}
/**
* Returns a map containing:
*
*- continent: Map containing:
*
- code: String
*
- names: Map of lang to translated name
*
- geoname_id: Long
*
*- country: Map containing:
*
- iso_code: String
*
- names: Map of lang to translated name
*
- geoname_id: Long
*
*- registered_country: Map containing:
*
- iso_code: String
*
- names: Map of lang to translated name
*
- geoname_id: Long
*
*
*
* @param ipAddress IPv4 or IPv6 address to lookup.
* @return A Map with the data for the IP address
* @throws IOException if there is an error opening or reading from the file.
*/
private Object get(InetAddress ipAddress,
String type) throws IOException {
String databaseType = this.getMetadata().getDatabaseType();
if (!databaseType.contains(type)) {
String caller = Thread.currentThread().getStackTrace()[2]
.getMethodName();
throw new UnsupportedOperationException(
"Invalid attempt to open a " + databaseType
+ " database using the " + caller + " method");
}
return reader.get(ipAddress);
}
/**
*
* Closes the database.
*
*
* If you are using {@code FileMode.MEMORY_MAPPED}, this will
* not unmap the underlying file due to a limitation in Java's
* {@code MappedByteBuffer}. It will however set the reference to
* the buffer to {@code null}, allowing the garbage collector to
* collect it.
*
*
* @throws IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
this.reader.close();
}
public String country(String ipAddress) throws IOException {
InetAddress ia = InetAddress.getByName(ipAddress);
Object o = get(ia, "Country");
if (!(o instanceof Map))
return null;
Map m = (Map) o;
o = m.get("country");
if (!(o instanceof Map))
return null;
m = (Map) o;
o = m.get("iso_code");
if (!(o instanceof String))
return null;
return (String) o;
}
/**
* I2P -
* Write all IPv4 address ranges for the given country to out.
*
* @param country two-letter case-insensitive
* @param out caller must close
* @since 0.9.48
*/
public void countryToIP(String country, Writer out) throws IOException {
reader.countryToIP(country.toUpperCase(Locale.US), out);
}
/**
* @return the metadata for the open MaxMind DB file.
*/
public Metadata getMetadata() {
return this.reader.getMetadata();
}
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: DatabaseReader geoip2-file.mmdb ip");
System.exit(1);
}
File f = new File(args[0]);
Builder b = new Builder(f);
b.withCache(new CHMCache(256));
DatabaseReader r = b.build();
System.out.println("Database Metadata: " + r.getMetadata());
String c = r.country(args[1]);
System.out.println("IP: " + args[1] + " country: " + c);
}
}