Home > groovy, programming > Groovy script to find country origin of IP

Groovy script to find country origin of IP

After listening to grails podcast #77, I was interested in some of the code snippets available around the site for the gr8conf “Conference dedicated to Groovy, Grails & Griffon and other Great technologies”. The direct link is http://www.gr8conf.org/blog/2009/02/02/5#comments. I was looking at code around utilising some of the webservicex.net webs services and in particular Geo IP Service for “Finding the visitors country by IP“. The example given was for a Grails service. I was interested to run it as a script.

Seems simple, cut, paste, run … class could not be run! Ok so I am trying it as a script from a command line. I need to either create a main or easier still just invoke the method from a command line groovy script

groovy -e "gips = new GeoIPService(); println gips.getCountryByContext()"
 
Caught: groovy.lang.MissingPropertyException: No such property: log for class: GeoIPService
at GeoIPService.getProperty(GeoIPService.groovy)
at GeoIPService.getCountryByContext(GeoIPService.groovy:9)
at script_from_command_line.run(script_from_command_line:1)
at script_from_command_line.main(script_from_command_line)

Ok so I need a logger library. To keep things simple I try java.util.logging.Logger and the method Logger.getAnonymousLogger() . Unfortunately this led to an exception along the lines of

Caught: groovy.lang.MissingMethodException: No signature of method: java.util.logging.Logger.warn() is applicable for argument types: (java.lang.String, groovy.lang.MissingMethodException) ...

java.util.logging.Logger is not compatible with the log4j logger used in grails which allows passing of a String and an Exception to the warn. Who needs logging anyway, this is a script right? next step was for me to uncomment the log statements and the script ran. Yeah! output was the disappointing statement

null

What? where did that come from? sure there are return null’s in the code returned from exception code but the exception is … silent :( … as I have commented out the log statements. Now in desperation I would just change them to println statements and be done with it but why not stay with the “logging” best of breed methodology. Back to trying to find a version of log4j lying around. In the end I found one in my maven home ~/.m2 . I suppose this raises the question should I be using maven or other build/dependency tool for building this simple learning script? is that too much for a script? TDD says not it ain’t. Just to make sure it runs I tried

import org.apache.log4j.*
 
CLASSPATH=$CLASSPATH:~/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar groovy -e 'gips = new GeoIPService(); println gips.getCountryByContext()'


null

damn, and this time with logging statements. Maybe it is time to put this into an IDE and get some suggestions as to what is going on. I give up on the TDD and start out putting print log statements through the code. I find the bug

def response = "http://www.webservicex.net/geoipservice.asmx/"+method.toURL().text

the toURL() method is only acting on part of the string, the method string, which fails to be a proper URL so it fails, but silently as it is in a method which throws exception and is only reported by a log.warn and by default my logging is set to the highest I presume log.fatal? now how to change the level of the logger? ok I should have guessed setLevel(org.apache.log4j.Level level) now where to set it? I do not have a constructor everything is pretty much static so I suppose I can just use a static block like so

static def log = Logger.getRootLogger()
static{
    log.setLevel(Level.INFO)
}

now I get the full pleasure of a groovy style exception

java.net.MalformedURLException: no protocol: GetGeoIPContext
at java.net.URL.<init>(URL.java:567)
at java.net.URL.<init>(URL.java:464)
at java.net.URL.<init>(URL.java:413)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.toURL(DefaultGroovyMethods.java:2479)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:51)
at org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:54)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:766)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:754)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:198)
at GeoIPService.getCountryByUrl(GeoIPService.groovy:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:78)
at GeoIPService.getCountryByContext(GeoIPService.groovy:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:778)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:198)
at script_from_command_line.run(script_from_command_line:1)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:756)
at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:778)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
at org.codehaus.groovy.runtime.InvokerHelper.runScript(InvokerHelper.java:401)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:749)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
at script_from_command_line.main(script_from_command_line)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:749)
at groovy.lang.GroovyShell.runMainOrTestOrRunnable(GroovyShell.java:244)
at groovy.lang.GroovyShell.run(GroovyShell.java:453)
at groovy.lang.GroovyShell.run(GroovyShell.java:433)
at groovy.lang.GroovyShell.run(GroovyShell.java:160)
at groovy.ui.GroovyMain.processOnce(GroovyMain.java:496)
at groovy.ui.GroovyMain.run(GroovyMain.java:308)
at groovy.ui.GroovyMain.process(GroovyMain.java:294)
at groovy.ui.GroovyMain.processArgs(GroovyMain.java:111)
at groovy.ui.GroovyMain.main(GroovyMain.java:92)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:101)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:130)

as opposed to what java would give me (here demonstrated with a groovy script)

groovy -e "println 'http://'+'GetGeoIPContext'.toURL()"
Caught: java.net.MalformedURLException: no protocol: GetGeoIPContext
at script_from_command_line.run(script_from_command_line:1)
at script_from_command_line.main(script_from_command_line)

which lets me take it one step further and confirm

groovy -e "println new String('http://'+'GetGeoIPContext').toURL()"
http://GetGeoIPContext

and back to the original,

def response = new String("http://www.webservicex.net/geoipservice.asmx/"+method).toURL().text

and as a complete script (heavily borrowing from the original Finding a users country by IP)

import org.apache.log4j.*
 
class GeoIPService {
 
    static def log  = Logger.getRootLogger()
    static{
        log.setLevel(Level.INFO)
    }
 
    def getCountryByContext() {
        try {
            return getCountryByUrl("GetGeoIPContext")
        } catch (Exception e) {
            log.warn "Exception in GeoIP service", e
            return null
        }
    }
 
    def getCountryByIp(String ipAddress) {
        try {
            return getCountryByUrl("GetGeoIP?IPaddress=" + ipAddress)
        } catch (Exception e) {
            log.warn "Exception in GeoIP service", e
            return null
        }
    }
 
    def getCountryByUrl(String method) throws Exception {
        def response = new String("http://www.webservicex.net/geoipservice.asmx/"+method).toURL().text
        def slurp = new XmlSlurper()
        def geoip = slurp.parseText(response)
        if (geoip.ReturnCode == 1) {
            if (geoip?.CountryName?.text() == "RESERVED") {
                return null
            } else {
                def country = geoip.CountryName.text()
                return country != null ? country[0].toUpperCase() + country[1..-1].toLowerCase() : null
            }
        } else {
            throw new Exception(geoip.ReturnCodeDetails.text())
        }
    }
    static void main(args) {
        def gisp = new GeoIPService()
        println args ? gisp.getCountryByIp(args[0]) : gisp.getCountryByContext()
    }
}

now for TDD and dependency management of log4j libraries and the like … maybe another day ;)

  1. No comments yet.
  1. No trackbacks yet.