Archive

Archive for February, 2009

Groovy script to find country origin of IP

February 8th, 2009 No comments

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 ;)

How to “stick” several PDF’s into 1 document

February 7th, 2009 No comments

I had some paperwork that I needed to scan and email out for my son’s kinder. Scanning was easy, even with some OCR, the scanner automatically created 4 separate PDF documents. Now to bring them into 1 document, hmm? Let’s try google.

Sure every man and his dog is suggesting methods under searches like “merge pdf documents” from how to articles that say to click on the “combine” menu item, which seems not to apply to my Mac all the way through to a bunch of shareware/freeware/paid software. But how do you know which is best? which one is safe? and which one is worth the purchase? for something this small nothing. I don’t want to go through the pain of working this out, trying some software, only to find out it will water mark the result or something.

Why not write it myself? I am playing around with the groovy programming language at the moment so why not try that? I took a look, “groovy pdf library” of course with a name like that I found a lot of groovy “really cool” PDFs. As Groovy works seemlesslly with Java and the JVM, next stop was searching for “java pdf library” a few other libraries made the front page but everything seemed to point to iText. It looks pretty comprehensive, there is even an “iText in action” book available. I start looking at the documentation and decide it is too much. I just want to merge 4 PDFs into 1 and I don’t want to use any more then 4 lines!

Time to have a look at the root of my scripting knowledge, Perl. Soon enough I found PDF::Reuse and its prDoc command. The result is

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/perl
use warnings;
use strict;
use PDF::Reuse;
 
prFile("kinderNewsletter.pdf");
 
prDoc('kinderScan_01.pdf');
prDoc('kinderScan_02.pdf');
prDoc('kinderScan_03.pdf');
prDoc('kinderScan_04.pdf');
 
prEnd();

I later also found that there is some unix command line tool pdftk which would have done the same but I would need to install that and work out how to use it, maybe next time.