Groovy and DNS query to get your external IP address

I stumbled upon this Stackoverflow question:

and got interested into the way to get your external IP by using a DNS lookup, as presented in this answer:

It’s an interesting idea and implementation and I also agree with the poster:

  • DNS query is a bit faster
  • More importantly, I have a feeling it’s more reliable. I know that sites can be slow, DNS query is less likely to be so
  • Given the investment in hosting the DNS service on a large scale, they service is likely more robust and likely to stay there longer than sites

Anyway, if you are using Oracle Java, then you already have a JNDI DNS provider built-in. You can read more about it here:

This Stackoverflow question:

specifically this answer:

has an example code to query a generic DNS server, provided you know it’s IP address. Below is a Groovy script that combines the information from the above links:

  • Gives an example of how a default way to perform a DNS query would be – getIp method
  • Gives a way to perform DNS query on the specific DNS server – getIpUsingDnsServer method
  • Uses these to query myip.opendns.com to get your external IP

Read more about OpenDNS myip entry here:

Here’s the script itself:

import javax.naming.Context
import javax.naming.NamingEnumeration
import javax.naming.directory.Attribute
import javax.naming.directory.Attributes
import javax.naming.directory.DirContext
import javax.naming.directory.InitialDirContext

class DnsPrb {
   static main(String[] args) {
      new DnsPrb().run()
   }
   
   def run() {
      String dnsServer = 'resolver4.opendns.com'
      String dnsIp = getIp dnsServer
      println("$dnsServer -> $dnsIp")
      
      def myIps = getIpUsingDnsServer dnsServer, 'myip.opendns.com'
      println("myIps: $myIps")
   }

   String getIpUsingDnsServer(String dnsServer, String name) {
      Properties env = new Properties()
      env.put Context.INITIAL_CONTEXT_FACTORY, 'com.sun.jndi.dns.DnsContextFactory'
      env.put Context.PROVIDER_URL, "dns://$dnsServer".toString()

      DirContext ictx = new InitialDirContext(env)
      String[] types = ["A", "AAAA"]
      Attributes attrs = ictx.getAttributes name, types

      NamingEnumeration<? extends Attribute> e = attrs.all
      def ips = e.collect { it.get() }
      ips
   }

   String getIp(String name) {
      InetAddress dnsInetAddress = InetAddress.getByName name
      String ip = dnsInetAddress.hostAddress
      ip
   }
}

Packaging for zero install

Here’s a quick tutorial on how to package things for Zero install. I am going to package the latest Groovy (2.0.4 at the time of this writing). There’s a tutorial on the site itself – check this.

Install 0publish-gui

I’m using Xubuntu 12.04, so I am going to install 0publish-gui – seems that for Windows you should use a different version, see the tutorial page for that. To install it, run this:

$ 0alias 0publish-gui http://0install.net/2007/interfaces/0publish-gui.xml

Creating a new feed

Start the program:

$ 0publish-gui

On the first dialog that opens:

click New feed.

Populate About tab

Populate the About tab like this:

All this is readily available from Groovy’s main site.

Versions tab

On the versions tab:

click Add Group, clcik OK to add an empty group, then add the Groovy binary archive by clicking Add archive button. You can find one from the Groovy’s download page, currently it’s http://dist.groovy.codehaus.org/distributions/groovy-binary-2.0.4.zip:

Click Download. After the zip has been downloaded:

click OK.

Version properties

Populate the version properties dialog like this:

Add dependencies

Groovy requires JDK, so let’s add that one as a dependency:

Click Add Requires:

From the Roscidus.com mirror search for “openjdk”. You should find this one. There’s a link under “Full name” section – the link is http://repo.roscidus.com/java/openjdk-jdk, so paste it here:

After clicking OK, you should have this:

To have the actual java command, openjdk-jre (http://repo.roscidus.com/java/openjdk-jre) is also needed, so add that, too:

Publishing tab

To publish the feed, enter the url and the key:

The above assumes you already have a GPG key to use for signing. If you do not have a GPG key generated, click on Add. This will open a GPG window, which you can populate like this (of course, change the personal information):

Saving the feed

Click Save now, you should get a confirmation of the files getting saved:

It will ask you for the GPG key password again:

After all that, you should have the following files:

total 27M
4.0K BC15B8C183209EDE.gpg
 27M groovy-binary-2.0.4.zip
4.0K groovy.xml
8.0K interface.xsl

Adding multiple commands

Exit 0publish-gui and edit the script to something like this:

<?xml version="1.0" ?>
<?xml-stylesheet type='text/xsl' href='interface.xsl'?>
<interface uri="https://icyrock.com/0install/ext/groovy.xml" xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>groovy</name>
  <summary>agile and dynamic language for the Java Virtual Machine</summary>
  <description>
Groovy...

is an agile and dynamic language for the Java Virtual Machine

builds upon the strengths of Java but has additional power features inspired

by languages like Python, Ruby and Smalltalk

makes modern programming features available to Java developers with almost-

zero learning curve

provides the ability to statically type check and statically compile your

code

supports Domain-Specific Languages and other compact syntax so your code

becomes easy to read and maintain

makes writing shell and build scripts easy with its powerful processing

primitives, OO abilities and an Ant DSL

increases developer productivity by reducing scaffolding code when

developing web, GUI, database or console applications

simplifies testing by supporting unit testing and mocking out-of-the-box

seamlessly integrates with all existing Java classes and libraries

compiles straight to Java bytecode so you can use it anywhere you can use Java</description>
  <homepage>http://groovy.codehaus.org/</homepage>
  <category>Development</category>
  <group>
    <needs-terminal/>
    
    <command name="grape" path="bin/grape"/>
    <command name="groovy" path="bin/groovy"/>
    <command name="groovyc" path="bin/groovyc"/>
    <command name="groovyConsole" path="bin/groovyConsole"/>
    <command name="groovydoc" path="bin/groovydoc"/>
    <command name="groovysh" path="bin/groovysh"/>
    <command name="java2groovy" path="bin/java2groovy"/>
    <command name="startGroovy" path="bin/startGroovy"/>

    <environment name="GROOVY_HOME" insert="." mode="replace" />

    <implementation id="sha1new=d0f6ef2a42b0f5479993e0dd4e5d66b622d18a99" license="The Apache License 2.0" main="bin/groovy" released="2012-09-30" version="2.0.4">
      <archive extract="groovy-2.0.4" href="http://dist.groovy.codehaus.org/distributions/groovy-binary-2.0.4.zip" size="28273120" type="application/zip"/>
      <requires interface="http://repo.roscidus.com/java/openjdk-jre">
        <environment name="JAVA_HOME" insert="." mode="replace" />
      </requires>
      <requires interface="http://repo.roscidus.com/java/openjdk-jdk">
        <environment name="JAVA_HOME" insert="." mode="replace" />
      </requires>
    </implementation>
  </group>
</interface>

Save the file, exit the editor, open 0publish-gui, open the feed and do the signing again and you now have multiple commands. E.g. to run groovysh instead of groovy, you can do this:

$ 0alias --command groovysh groovysh https://icyrock.com/0install/ext/groovy.xml

Publishing on the Web server

The only thing needed at this point is to publish them on your Web server. You need only the three files generated by the program, not the archive itself – the archive will be downloaded from the URL you gave (so in this case Groovy’s main site) and it will be checked to confirm it did not change (thus all the SHA info in the groovy.xml file if you open it). This depends on the server you are using, but usually just putting it in the required folder should be sufficient. Obviously, the URL should match what was entered on the Publishing tab.

Testing the feed

When you install on the Web server (you can even test on your local server for that matter), you can test by using 0alias as usual:

$ 0alias groovy https://icyrock.com/0install/ext/groovy.xml
$ 0alias --command groovysh groovysh https://icyrock.com/0install/ext/groovy.xml

You should be prompted with an “unknown key” dialog:

Make sure the key fingerprint is the one you trust. After you trust a key, all programs signed with that key will be trusted for installation, so you better trust the owner of the key…

After accepting the key and installing, you should be able to use groovy or groovysh:

$ groovy --version
Groovy Version: 2.0.4 JVM: 1.7.0_04 Vendor: Oracle Corporation OS: Linux
$ groovysh -V
Groovy Shell 2.0.4

Adding to the feed search engine

As per this, you are able to add your programs to this feed list. This is a good way to contribute to 0install, so if you package someting, announce it there.


Groovy SwingBuilder

I’ve been playing with Groovy’s SwingBuilder lately. It allows you to build Java Swing interfaces very succinctly and in a natural hierarchical way. For example, here’s how you would make a sample frame with a few controls in it:

import groovy.swing.SwingBuilder;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

def sb = new SwingBuilder()
sb.frame(title: 'Sample SwingBuilder app',
         location: [400, 50],
         pack: true,
         show: true,
         defaultCloseOperation: JFrame.EXIT_ON_CLOSE) {
  gridLayout(columns: 2, rows: 4)
  label('First name:')
  textField(id: 'firstName')       

  label('Last name:')
  textField(id: 'lastName')

  label('Age:')
  textField(id: 'age')

  button(text: 'Show', actionPerformed: {
    sb.optionPane().showMessageDialog(null, 
      "Hello ${firstName.text} ${lastName.text}. You are ${age.text} years old.", 
      "Data you entered",
      JOptionPane.INFORMATION_MESSAGE)
  })
}

Save the above in SwingTest.groovy and run it with groovy SwingTest.groovy. You will get something similar to this:

Compare this to the code. In the code, we first set the layout to be grid layout with 2 columns and 4 rows. Then we have three label-text field rows (label goes into the first column and text field into the second column). We put the button in the first column of the last row.

It’s very simple and clean, no additional cruft. Some highlights:

  • Label text is the default property, so label('Hello') will create the label with it’s text set to Hello. This applies to other components, too.
  • Properties are auto-wired, so doing textField(id: 'abc') will make a text field and set it’s ID property.
  • You will note ID is not a default property of JTextField. This is the addition by SwingBuilder, allowing it’s use later. This is how firstName.text works inside the message string.
  • You will notice the names of the components correspond to Java class names most of the time. To get the name, drop J and camel-case it: ** JTextField -> textField ** JButton -> button ** JOptionPane -> optionPane ** etc.

When you press show, a standard JOptionPane message dialog will appear:

showing the usage of component IDs and another SwingBuilder-wired component, optionPane.

SwingBuilder is Groovy

The most important thing is that all the code in the above example is pure Groovy. That is, you can use all normal Groovy constructs to do what you need. For example, you can do the following:

import groovy.swing.SwingBuilder;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

def sb = new SwingBuilder()
sb.frame(title: 'Sample SwingBuilder app',
         location: [400, 50],
         pack: true,
         show: true,
         defaultCloseOperation: JFrame.EXIT_ON_CLOSE) {
  def noItems = 5

  gridLayout(columns: 2, rows: noItems + 1)

  (1..noItems).each {
    label("Item $it:")
    textField(id: "item$it")       
  }

  button(text: 'Show', actionPerformed: {
    def items = (1..noItems).collect {
      "Item $it: " + sb."item$it".text
    }.join("\n")
    sb.optionPane().showMessageDialog(null, "Your items:\n$items",
     "Data you entered", JOptionPane.INFORMATION_MESSAGE)
  })
}

Save the above as SwingTest2.groovy and run it with groovy SwingTest2.groovy. You will get something like the following:

If you take a look at the above code, you will spot that regular groovy constructs are used:

  • def noItems = 5 defines the number of items we want shown
  • The following:
(1..noItems).each { 
  ... 
} 

is a regular Groovy Range + each method call

  • The following:
def items = (1..noItems).collect {
  "Item $it: " + sb."item$it".text
}.join("\n")

is again a Range + collect method call, also using the regular Groovy variable references (referencing sb builder and items in it with item$it).

If you enter the data as the above, clicking on Show button will yield:

All in all, I don’t think you could get much closer to a clean programmatic interface then this.


Automatic testing in Groovy from command line

If you have a need to do some automatic testing from the command line, Groovy can be of much help. Here’s a quick way to set this up. First, make sure you have both Groovy and Ant installed and the respective libraries in your PATH or CLASSPATH. For example, if you don’t have Ant installed, you can get exceptions such as:

Caught: java.lang.NoClassDefFoundError: org/apache/tools/ant/DemuxInputStream
  at groovy.util.FileNameFinder.class$(FileNameFinder.groovy)
  at groovy.util.FileNameFinder.$get$$class$groovy$util$AntBuilder(FileNameFinder.groovy)
  at groovy.util.FileNameFinder.getFileNames(FileNameFinder.groovy:37)
  at groovy.util.FileNameFinder.getFileNames(FileNameFinder.groovy:29)

Provided you have the above set up properly, here’s how you can make a simple test suite:

// Save this as AllTests.groovy
def ats = new groovy.util.AllTestSuite()
def suite = ats.suite("test", "**/*Test.groovy")
junit.textui.TestRunner.run(suite)

The above makes the AllTestsSuite, tells it to search the tests in “test” folder and also tells it to only consider files that match “*Test.groovy” (e.g. OneTest.groovy, MyCalculatorTest.groovy, etc.). Now, let’s make the test folder and put two sample tests in it:

// Save this to test/OneTest.groovy
class OneTest extends groovy.util.GroovyTestCase {
  void testOne1() {
    println("testOne1")
    assert 1 == 1
  }
  void testOne2() {
    println("testOne2")
    assert 1 == 1
  }
}

// Save this to test/TwoTest.groovy
class TwoTest extends groovy.util.GroovyTestCase {
  void testTwo1() {
    println("testTwo1")
    assert 1 == 1
  }
  void testTwo2() {
    println("testTwo2")
    assert 1 == 1
  }
}

If you now run this:

$ groovy AllTests.groovy

it should run all your tests and print something like this:

.testOne1
.testOne2
.testTwo1
.testTwo2

Time: 0.012

OK (4 tests)

Note that it’s not usual to use println in your tests – the above is just to make it obvious that the test suite we made executes all the tests. A good thing about this solution is that you don’t need to update the list of tests. If you want to make another test case, just make another file (e.g. LottoWinnerTest.groovy) and put it in test folder. AllTestSuite will find it automatically. Also, if you ever need to remove some of your tests, simply move them from test folder to some other location. All is very DRY.