Last night I downloaded MaxMind's GeoLite City database and Java API. I wanted some way to figure out where the visitors to my web site were located around the world, and potentially use the Google Maps API to plot locations on my site. MaxMind produces a database that contains location information keyed by IP address and makes available APIs in Java, C, Ruby, Perl, Python and other languages. I was aware of MaxMind's product through some web searches for GPS location by latitude/longitude I performed a few months ago. I chose to download the free, lite, version of the database as a start. The paid version, GeoIP City, is a bit pricey for the small scope of my at-home projects at $370. If I decide to upgrade, the database is just a drop-in replacement and the Java API remains the same. The Java API is distributed under the GPL.
For a number of reason I am not 100% satisfied with Webalizer as a server log analyzer. One of the reasons, which is due to user error I'm sure, is that I can not get reverse-DNS lookups to work. I also don't want to run webalizer daily over my log files and end up with disjointed information in the files and directory structure needed to support it. Additionally I'd like to be able to display this information in real-time on the web.
The GeoLite City database is a 25 MB file that is placed somewhere on the filesystem where the application can access it.
Using the MaxMind Java API is straight forward. There are three main data structures classes in the com.maxmind.geoip package: Country, Region and Location. Location contains the most information, containing a superset of both Country and Region. Also in the package is LookupService, the largest class in the package, that contains methods for selecting the database to use, whether to read it from disk or RAM and looking up IP address info. I imported the classes into Eclipse 3.2 and started an EJB project. For this project I'm going to use Hibernate to handle database persistence.
I created a Session Bean called GeoIP that has a LookupService member. In the ejbCreate() method I have:
try {
lookupService = new LookupService("c:\\GeoLiteCity.dat");
}
catch (Exception e) {
e.printStackTrace();
}
This LookupService constructor throws an IOException and the catch block is completely inadequate for a professional application so make sure you handle it in whatever way you deem fit. The good news is that this is all the setup code needed before making queries against the database. In the method getAndSaveIPInfo(String ipAddr) the first line is:
Location loc = lookupService.getLocation(ipAddr);
The one complaint I have about the Location class is that it does not conform to the JavaBean specification and as such there are no getter and setter methods for the public member fields. Hibernate populates and persists objects using the getter and setter methods so I extended Location and gave it getters/setters for all public fields and added id and ipAddr fields with their own getters/setters. Since it is only a bean, rather than waste space listing the code for the LocationBean class I will only list the definition.
public class LocationBean extends Location implements Serializable
After creating a LocationBean and calling the LookupService the LocationBean has to be populated. The method to get the Location object is the LookupService#getLocation(String ipAddr). This method is overloaded to get an InetAddress and long as well.
Location loc = lookupService.getLocation(ipAddr); LocationBean bean = new LocationBean(); bean.setArea_code(loc.area_code); bean.setCity(loc.city); bean.setCountryCode(loc.countryCode); bean.setCountryName(loc.countryName); bean.setDma_code(loc.dma_code); bean.setLatitude(loc.latitude); bean.setLongitude(loc.longitude); bean.setPostalCode(loc.postalCode); bean.setRegion(loc.region);
I'm using MySQL 5.0 to store the data in LocationBeans. The DDL to create the table and the Hibernate mapping file look like this:
mysql> create table geoip_locations (
-> id integer primary key,
-> ipAddr varchar(15),
-> area_code tinyint,
-> city varchar(100),
-> countryCode char(2),
-> countryName varchar(100),
-> dma_name varchar(100),
-> latitude float,
-> longitude float,
-> postalCode varchar(25),
-> region varchar(100)
-> );
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.anthonychaves.geoip.LocationBean" table="geoip_locations">
<id name="id">
<generator class="native"/>
</id>
<property name="area_code"/>
<property name="city"/>
<property name="countryCode"/>
<property name="countryName"/>
<property name="dma_code"/>
<property name="latitude"/>
<property name="longitude"/>
<property name="postalCode"/>
<property name="region"/>
</class>
</hibernate-mapping>
After putting the table and mapping file in place the code to save the LocationBean is just standard Hibernate stuff. This code must be refactored out of the getAndSaveIPInfo(…) method into a new method or into a helper class. I'll take care of the this weekend though.
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); session.save(bean); session.getTransaction().commit(); sessionFactory.close();
That is about all I have for right now. I'll continue working on this project and web client and post any updates I make. Thanks for reading and comments are always welcome.





3 comments ↓
Hi Anthony,
I’m working on a similar project for our website. How did you get the GeoLiteCity.dat file into a MySql database?
I looked at the file and could not import it into anything (Access, MSSQL, Excel) because .dat could be any format.
Unfortunately I don’t program in Java or C so you lost me when you got to Eclipse 3.2.
Would you Export the MySql to a comma delimited flat file and zip it up for me?
I would really appreciate your help.
Thanks,
Jim
Hi Jim,
I’ll write up the instructions for importing into a MySQL database tomorrow morning.
What is your email address so I can notify you of when it’s finished?
Thanks for replying!
Anthony
A few questions:
Could you post the code you used to import that .dat file to mysql?
Did you ever manage to get GeoLiteCity to work with geo-webalizer? I have been able to tweak my version of geo-webalizer and like it, however I am disappointed in that it only shows GeoCountry info and I can’t get it anymore granular to the city level. I also configured AWstats and was able to get it to work with GeoCountry and GeoLiteCity. I am now in the process of writing my own scripts to capture real time users, those logged in versus those just surfing using the GeoCityLite and perl’s Geo::IP.
Thanks.
Leave a Comment