Archive for July, 2006

MaxMind's GeoLiteCity database and Java API

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.

Java: Follow-ups

In regard to my last post, Christian Ullenboom recommended that I put the JBoss naming properties in a separate jndi.properties file that is in the runtime classpath. This is a great recommendation because it decouples the naming properties from the code allowing different naming services to be used. I think doing it this way is the “right” way to do it because the Java code is cleaner and the application becomes more flexible. There was one small problem I ran into after putting the jndi.properties file into my Eclipse project. Running the application produced a NoInitialContextException and it seemed like the jndi.properties file was not found in the project classpath. I “fixed” this by exporting a jar file containing the properties file and manually added that jar file to the project classpath. I'm not sure I like doing it that way and I'll play with it a little more to see if I can make due without any artificial external jars.

Tom Hawtin had some advice about (not) overriding the Object#finalize() method. Here's what the code should look like using Java 1.5:

@Override
protected void finalize() throws Throwable {
    super();
    System.out.println("This is the com.anthonychaves.scratch.Guitar#finalize() method.");
}

I wasn't really sure what the @Override annotation meant, other than it was an annotation new to J2SE 1.5. A search on the
Java 1.5 docs told me what the @Override annotation means. A complie-time error would be nice when refactoring a superclass removes a method that is expected to be overridden in a subclass. The only problem I see is that you have to be aware of @Override to use it. Thank you for pointing it out to me Tom.

David Shay pointed out an oversight I made coding up the Guitar classes a few weeks ago. I was trying to illustrate that changing the value of a final variable was not possible using the = operator and used String#replaceAll(…) instead. David correctly said that the nameBrand.replaceAll(“Fender”, “Celebrity”); line of code would not work as I expected and had I bothered to read that variable again I would have caught it. Since Java Strings are immutable a call to replaceAll(…) doesn't operate on the String it's called on, but rather returns a new String. No matter how you try it, you can't change a final String.

Thanks all that wrote comments. They're always welcome.

The Importance of InitialContext(Hashtable environment)

This week I have been writing a simple J2EE application client. I haven't written an app client in a while so I decided to try it out using my freshly downloaded Eclipse 3.2 Callisto with J2EE Tools. While I was working on this project I ran into a couple problems with javax.naming.InitialContext. These were not problems with the class itself but rather my inability to use it as intended. To illustrate these Callisto context-newbie mistakes I'll walk through the code for a small sample app that runs on JBoss 4.

For this project I decided to use XDoclet to help create the EJB classes and interfaces rather than manually code them all by hand. Before I could use it I had to download it from since Eclipse 3.2 does not come with it. Eclipse needs to know where XDoclet is installed so I set it in Window -> Preferences -> XDoclet.

After coding the EJB the J2EE Tools make it trivially easy to create a client jar that contains the classes required for a client to use the bean. Just right-click the EJB project in the Project Explorer view and go to EJB Client Jar -> Create EJB Client Jar. When a client is created the J2EE Tools creates a Util.java file that contains some helper methods used to locate and create an instance of an EJB. This should be a decent time saver and keep people from doing boiler plate code that leads up to PortableRemoteObject.narrow(…). One of the methods is getHome() which gets the home interface used to create the bean.

With this util class making the client should be a breeze. The hard work is reduced to:

FibonacciHome fibHome = FibonacciUtil.getHome();
Fibonacci fib = fibHome.create();
fib.getFibonacciNumber(12);

Unfortunately running this code produces a nasty stack trace.

Exception in thread "main" javax.naming.NoInitialContextException:
 Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
	at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
	at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
	at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source)
	at javax.naming.InitialContext.lookup(Unknown Source)
	at net.anthonychaves.ejb.FibonacciUtil.lookupHome(FibonacciUtil.java:22)
	at net.anthonychaves.ejb.FibonacciUtil.getHome(FibonacciUtil.java:42)
	at net.anthonychaves.ejb.MyClient.main(MyClient.java:24)

A closer look at the getHome() method shows that it calls lookupHome(…)

   public static net.anthonychaves.ejb.FibonacciHome getHome() throws javax.naming.NamingException
   {
      if (cachedRemoteHome == null) {
            cachedRemoteHome = (net.anthonychaves.ejb.FibonacciHome) lookupHome(null,
               net.anthonychaves.ejb.FibonacciHome.JNDI_NAME,
               net.anthonychaves.ejb.FibonacciHome.class);
      }
      return cachedRemoteHome;
   }

And lookupHome(…) takes as its first argument a Hashtable of JNDI environment values keyed by javax.naming.Context constants. The problem was that getHome() was passing null to lookupHome(…). When creating an InitialContext the environment values to use come from one of two places: the environment Hashtable passed in to the InitialContext constructor and a JNDI properties file, in that order. That NoInitialContextException was caused because my project did not pass an environment Hashtable and I did not have a JNDI properties file in my project. I decided to fix this problem by looking in the JNDI Tutorial to find the options required for getting the InitialContext of my EJB from the JBoss naming server.

I was fairly certain I only needed two environment properties set in order to gain access to my EJB. The Naming Operations page told me I needed to set values for Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL. According to the Environment Overview the Context.INITIAL_CONTEXT_FACTORY value should be a class that implements the InitialContextFactory interface. Because I'm using JBoss I thought it would be worthwhile to head over to the JBoss Application Server Guide and look at the Naming on JBoss section. That sections told me the environment Hashtable should look like the following:

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "localhost:1099");

The rest of the client doesn't change other than using the getHome(Hashtable environment) method in the *Util class instead of the no-arg getHome() method. And with that, the client can make a connection to the JBoss server and use the EJB. I really appreciate all the hard work the guys at the JST and WST projects do to create such a robust and flexible IDE that makes writing app clients so much easier than it has in the past. Using the tools is no substitute for creating a few EJBs and clients without tools, but they make life easier once you have a few under your belt. I'm really happy with Eclipse 3.2 as my IDE at this point.

I'm not an expert on the JNDI libraries so please check out the links above to get a better feel for all that it can do.

In other news, LinkieWinkie has found me. Here's a wink right back.

Ruby Modules, Mixins and name clashes

Ruby allows the creation of modules that provide namespaces for methods and constants to live. They are also used to mixin these methods and constants into classes. These mixed-in modules provide a class with references to whatever is defined within the module. Below is a small (meaningless) Ruby module.

module AnthonyModule
  CONST_DATA = "I am a constant from AnthonyModule"

  def who_am_i
    puts "My name is: #{@name}"
  end

  def ten_times write_me
    10.times { puts "#{write_me}" }
  end
end

This module provides two methods and one constant. When this module is mixed in with a class these methods will become instance methods of the class. Below is an example of using the require method to load the AnthonyModule.rb file and the include method to reference the module in the class.

require "AnthonyModule"

class MyClass
  include AnthonyModule

  def initialize name
    @name = name
  end

end

Notice this class does not have a method called ten_times defined in it. However when an instance of this class is created it will have a ten_times method because the AnthonyModule is included. A call to this method is shown below.

m = MyClass.new "Instance 1"
m.ten_times "Hello modules!"

This will produce ten lines each with “Hello modules!” printed on it. Now what happens if another module is introduced that provides another method also named ten_times?

module NewModule
  CONST_DATA = "I am a constant in newmodule.rb"

  def ten_times multiply_me
    puts "ten times #{multiply_me} is: #{ 10 * multiply_me }"
  end
end

Using require and include to mix in NewModule into the class there are now two methods with the same name and signature in the class. Because Ruby looks in the module included last in a class it will find the NewModule#ten_times method before the AnthonyModule#ten_times method. Running the sample code gives an error now.

ruby/NewModule.rb:5:in `*': String can't be coerced into Fixnum (TypeError)

The problem here is that Ruby does not know how to multiply a number by a string. I don't want to do this though, I want to print out my message ten times. Because I'm calling the ten_times method outside of the class I can't specify exactly which ten_times method I want to call. Ruby picks the first one it finds searching backwards (upwards) through the include list. It appears as though using mixed in methods with a name clash outside of the class can not be done. If the module's methods were called in some class methods the module name could be used to differentiate between which method should be called. Outside the class there is no such luck.

I'll be starting a Rails app later.

Anthony