2010/10/15

Using BaseX to grep through Charles output

BaseX is a fantastic tool to grep through large XML files by creating indices for text, attributes and path summaries. I use this to analyze data generated by Charles, an HTTP proxy / HTTP monitor / Reverse Proxy. Both tools are written in Java, so you should have no problems running them. While the latter is not OSS, it comes at a reasonable price and may be used in a trial version for 30 days after which you get a nagging dialog.

Charles may be used as a simple tool to run stress tests. Just choose it as a proxy, run your usual usecases and export the data to xml. Two of the power features Charles offers are Man in the middle for SSL connections by importing the Charles Root CA certificate and modifying your requests on the fly to use test systems of new software instead of the live ones. Afterwards you may check the output by grepping your expected results using BaseX using XQuery or XPath. An example:

  • Start Firefox creating a new profile /Applications/Firefox.app/Contents/MacOS/firefox-bin -profileManager called Charles.
  • Download and install Charles' Firefox extension by visiting the download site and restart Firefox after installation.
  • In the Tools menu of Firefox Charles offers to install the CA certificate.
  • Make sure you have Charles running and choose to proxy Firefox in it's Proxy menu.
  • Enable Charles in the Tools menu of Firefox, now you should see requests coming through Charles.
  • Search for hgkit in Google.
  • Drill down in Charles tree view and find the http://www.google.de/search?client=firefox-a&rls=org.mozilla%3Aen-US%3Aofficial&channel=s&hl=de&source=hp&q=hgkit&meta=&btnG=Google-Suche request.
  • From the context menu of this request choose Repeat advanced and enter 100 iterations with 5 concurrent requests making sure to use a new session.
  • Export the new session in Charles as google-hgkit.xml.
  • Now start BaseX and create a new database referencing google-hgkit.xml. If you encounter an error Invalid byte 1 of 1-byte UTF-8 sequence make sure to use the built in parser in the Parsing tab. Make sure you chose Options/Realtime execution.
  • Analyze your data:
    • A search for /charles-session/transaction should result in 100 hits.
    • A search for /charles-session/transaction/response[@status="200"] should result in 100 hits.
    • As the html returned by the search is escaped in the body, you need to use XML escaping in your search through the body.
    • A search for /charles-session/transaction/response[@status="200"]/body[contains(text(), "this_surely_will_not_show_up_will_it_dsddada")] should result in 0 hits.
    • A search for /charles-session/transaction/response[@status="200"]/body[contains(text(), "<a href="http://hgkit.berlios.de/")] should result in 100 hits.
    • The Xquery for $y in (for $x in /charles-session/transaction where $x/response/@status="200" return ( $x/@endTimeMillis - $x/@startTimeMillis)) order by $y descending return $y will return the times for successful requests in milliseconds in descending order.
    • Return all requests taking more than 300 milliseconds: for $y in (for $x in /charles-session/transaction where $x/response/@status="200" return ( $x/@endTimeMillis - $x/@startTimeMillis)) where $y > 300 order by $y descending return $y.
    • Return the count for the above requests:
      let $times := (for $x in /charles-session/transaction 
        where $x/response/@status="200" 
        return ($x/@endTimeMillis - $x/@startTimeMillis))
      let $slowQueries := for $y in ($times) where $y > 300 return $y
      return count($slowQueries)
      

You could try to trigger a second search with a different searchterm and analyze that the search results are not mixed up by querying, e.g.
/charles-session/transaction[contains(@query, "q=hgkit")]/response[@status="200"]/body[contains(text(), "<a href="http://hgkit.berlios.de/")]. You may select more than one request in Charles for repetition. For further instructions on XPath I recommend w3school's tutorial.

2010/09/27

Starting an Android emulator automatically on MacOSX after Login via LaunchAgent

Homebrew offers a simple means to install additional software packages on your MacOSX computer. After initial installation of brew as admin user execute:

brew install android-sdk # will install the newest SDK starter package
android update sdk # this will open the UI, now install all platforms
chgrp -R staff /usr/local/Cellar/android-sdk/r7 # otherwise the ANDROID_HOME will be owned by the wheel group and you may not start anything as non admin user.

To use tools like the emulator add ANDROID_HOME and ANDROID_SDK_ROOT to your $HOME/.profile or $HOME/.bash_profile (if the latter exists, use this):

ANDROID_SDK_ROOT=/usr/local/Cellar/android-sdk/r7
ANDROID_HOME=$ANDROID_SDK_ROOT
export ANDROID_SDK_ROOT ANDROID_HOME

Create an emulator called Wildfire using the android command. Now if you want the emulator to be started automatically after you login, put the following into $HOME/Library/LaunchAgents/emulator-wildfire.plist:

After you saved the file, execute launchctl load $HOME/Library/LaunchAgents/emulator-wildfire.plist. From now on the emulator starts whenever you (or your CI user) logs in.

2010/09/05

A simple way to get a git hash as version info into Android applications using Maven

I recently decided to do some Android programming. Enters Mittagstisch KA. I really like to know which sources applications are built from. Using Maven and it's Antrun-Plugin this is rather simple:

This will create a new string resource file, which is automatically picked up by Android's resource compiler and might be read in your application by an Activity like this:


final String gitHash = getResources().getString(R.string.info_githash);

Do not forget to add res/values/githash.xml to your .gitignore file otherwise you will be committing infinitely :-).

2010/07/06

Really using launchctl to restart a Hudson Mac OS X build slave connected via JNLP automatically

In my last posting I wrote commands put into $HOME/.launchd.conf would be launched automatically after a login as stated by the man page for launchtctl. However this is false! After a reboot or relogin the commands will not be picked up! Stating man 5 launchd.conf:
$HOME/.launchd.conf  Your launchd configuration file (currently unsupported).
Pulling my ear: always try and test what you write about, sorry :-(. However using the following .plist file put into
$HOME/Library/LaunchAgents/org.hudson-ci.jnlpslave.plist really starts the slave:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>KeepAlive</key>
        <true/>
        <key>Label</key>
        <string>org.hudson-ci.jnlpslave</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/bin/java</string>
                <string>-jar</string>
                <string>/home/hudson/bin/slave.jar</string>
                <string>-jnlpUrl</string>
                <string>http://SERVER/hudson/computer/NODE/slave-agent.jnlp</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
</dict>
</plist>
Of course you have to adapt the path to your slave.jar as well as the URL to your Hudson-master.

2010/07/02

Using launchctl to restart a Hudson Mac OS X build slave connected via JNLP automatically

$HOME/launchd.conf does not work, see my working followup on this!

In my company's build infrastructure most of the slaves are located in the same data centre as the master. So we usually just use ssh to launch the slave.jar. As we did not want to buy XServe and our operations team would not like to host such an aberration from the usual (Linux) to build the handful of jobs which are Mac OS X only, we bought a Mac-Mini, which is part of the workstation LAN, where hudson will login automatically as we need the GUI anyway for Selenium tests.

As we have very strict firewall rules, access from the server LAN into the workstation LAN is forbidden. That's why we use JNLP to start the slave. So we've usually restarted the slave manually after the connection broke down.

Enters launchctl. Instead of fiddling around with a plist file I just used launchctl submit to achieve the same. From the commandline enter the following command:

launchctl submit -l hudson-slave -- /usr/bin/java -jar /Users/hudson/slave.jar -jnlpUrl http://SERVER:PORT/hudson/computer/NODE/slave-agent.jnlp

This will start the slave and restart it automatically if the connection ever should break down. You may watch the logging statements uttered by the slave by executing
open /Applications/Utilities/Console.app. To enable this command every time your Mac OS X machine reboots, create a .launchd.conf in the hudson user's HOME like this:


cat > /User/hudson/.launchd.conf << EOF
submit -l hudson-slave -- /usr/bin/java -jar /Users/hudson/slave.jar -jnlpUrl http://SERVER:PORT/hudson/computer/NODE/slave-agent.jnlp
EOF

You must not use the javaws way (the -wait option did not work for me), as the parent process will exit after it launched the jnlp connection and launchd will try to restart it again immediately for some times.

2010/06/27

Cross browser CSS and selectors - improving Hudson's viewList

After visiting the JBOSS Hudson instance with Firefox I really liked the way how the tabs were shown in the viewList. However revisiting the same page with Chrome was a disappointment. Neither was the active view emphasized nor were the inactive views flowing like they did with Firefox.

After some trials I detected that the attribute selectors in the CSS were not triggered. Digging into element view showed that Chrome did not render a whitespace between the height attribute, so Firefox rendered tr[style='height: 3px;'] while Chrome was rendering tr[style='height:3px;']. After duplicating the selectors and changing some attributes for Chrome I got at least the active view rendered in the right way, see jboss-style.css.

2010/06/18

Storing your OpenOffice, Xmind, ... zippy documents more efficiently in a SCM

I really like having my sourcecode and documents in a SCM since I first discovered CVS about 14 years back and introduced it in two companies thereafter, one of which had tried to use VSS (not really usable at the time, you had to lock files for editing which made you call for the VSS admin when your colleague was not available and did not allow to work on the same document at all), while in the other developers only had been using timestamped ZIP files before, which made team work really hard. In my current company I (maybe) made a mistake by pushing the switch from CVS to SVN about five years ago.
Back then I took a look at one of the first DVCS systems (arch) but found it to be to confusing (at least for me, YMMV). About three years ago I discovered Mercurial and really have liked it since, especially as I really like Python. I tried Bazaar as well because it promised better integration with Subversion but it used several different, incompatible repository formats so I had problems even checking out a remote repository more than once and the speed was not at all convincing as well. Nowadays I use Git sometimes which I like as well and I am especially impressed by the simple underlaying concept of storing things. However I still feel more comfortable with Mercurial right now and use Bitbucket a lot.
After having used DVCS you feel almost crippled by SVNs bad merging support and the idea of having no distinction between branches and tags seems not so clever anymore, we have had some hard times using standard SVN tools after a decision to put release tags in a directory called releases and are sometimes still struggling to find a common point of view on the correct position of trunk and what to store beneath release tags in repositories used by more than one project, so they are unambiguous both for our tooling chain and understandable for humans.
Well, back to the topic: nowadays a lot of software uses ZIP containers to store their information, which will bloat your SCMs because every new zip is so different from it's ancestor, even if you did only include a single new word, because the compression and a preview picture will make the new version very different from the old one. So I wrote a little Python script which will uncompress, delete the included preview and put the remaining files back into an uncompressed ZIP again using the stored method.