amazing development ruby, java and the rest 2012-06-02T09:57:04Z http://amazing-development.com/feed/atom/ WordPress Frank Spychalski http://amazing-development.com <![CDATA[Unblock-us for Mac OS]]> http://amazing-development.com/?p=540 2012-06-02T09:57:04Z 2012-03-07T19:32:04Z I’ve been using unblock-us for some time (update: at the time I wrote this article I was creating a trial account every week, but after a few months of flawless service I decided it’s time to become a paying customer, which I am since April) and took only a few precautions like not signing in to any service but hulu.com while using this other DNS service.

Yesterday Damon wrote a great article on how to secure your system for unblock-us and I followed his lead.

My only machine runs Mac OS, so I had to do a bit of research…

Step 1: run bind

Following these instructions:


sudo -s
rndc-confgen -b 256 > /etc/rndc.conf
head -n5 /etc/rndc.conf | tail -n4 > /etc/rndc.key

Make sure the ports match.


launchctl load -w /System/Library/LaunchDaemons/org.isc.named.plist
echo "launchctl start org.isc.named" >> /etc/launchd.conf

Step 2: configure

I didn’t need any fancy setup, so I pasted everything into /etc/named.conf:


//
// Include keys file
//
include "/etc/rndc.key";

// Declares control channels to be used by the rndc utility.
//
// It is recommended that 127.0.0.1 be the only address used.
// This also allows non-privileged users on the local host to manage
// your name server.

//
// Default controls
//
controls {
 inet 127.0.0.1 port 54 allow {any;}
 keys { "rndc-key"; };
};

options {
 forwarders {
  8.8.8.8;
  8.8.4.4;
 };

 listen-on-v6 { ::1; };
 listen-on { 127.0.0.1; };
};

zone "hulu.com" {
 type forward;
 forwarders {
  208.122.23.22;
  208.122.23.23;
 };
};
zone "unblock-us.com" {
 type forward;
 forwarders {
  208.122.23.22;
  208.122.23.23;
 };
};

Step 3: verify

Check for typos in the configuration:

named-checkconf /etc/named.conf

and check if we get new results. Before nslookup returned:

$ nslookup hulu.com
Server: 10.255.255.4
Address: 10.255.255.4#53

Non-authoritative answer:
Name: hulu.com
Address: 63.150.131.26
Name: hulu.com
Address: 63.150.131.11

Now I get:

# nslookup hulu.com
Server: 127.0.0.1
Address: 127.0.0.1#53

Non-authoritative answer:
Name: hulu.com
Address: 184.154.113.147
Name: hulu.com
Address: 50.22.86.53
Name: hulu.com
Address: 173.208.155.19
Name: hulu.com
Address: 173.208.170.19

]]>
0
schlumpf http:// <![CDATA[How to set up your own dyndns server (simple!)]]> http://amazing-development.com/?p=524 2012-02-18T10:33:43Z 2012-02-13T21:03:25Z Last week I was asked whether I knew how to set up a dyndns server. Well, I didn’t at that time, but I did some research. This is what I found.

First, problem description.

  • At home,you have a FritzBox or similar DSL router that lets you specify a dnydns server.
  • For some reason, you don’t want to use one of the predefined services, e.g. dyndns.org.
  • But you have a webserver somewhere in the internet with a domain and some webspace where you can run PHP scripts.
  • You don’t care whether the real DNS entry (domain name service) is changed, as long as your customers or whoever gets to see the right page. So this is not going to be a real dynamic DNS entry, but just some sophisticated redirect mechanism.

My FritzBox allows to specify a user-defined server to use for DynDNS. But I didn’t know how it would notify this server about my home IP address. I couldn’t find any documentation about this anywhere. So I wrote a script to print all the information about the request into a log file. This is what I found:

  • The FritzBox lets you specify username and password for the dyndns server, but neither is transmitted in the request parameters (GET or POST). This led me to think of HTTP authentication instead (authentification is needed if you want to control who can set the IP address for dyndns).
  • Also, the FritzBox’s IP address is not transmitted in the request parameters, but this is superfluous as you can get it from the server parameters ($_SERVER["REMOTE_ADDR"] in PHP).

So what do you need?

First, a .htaccess file to specify authentication for the dyndns script:

# put the name of the PHP script you're about to write here: 
<Files "dyndns.php">
   AuthType Basic
   AuthName "Authentication for clients that want to connect to this dyndns server." 
   # the name of the password file (see below): 
   # Specify the absolute path, or the path relative to the server root! 
   AuthUserFile /full/path/my-passwords
   # the username of your choice: 
   require user dyndnsuser
</Files>

You need to create the my-passwords file using the htpasswd utility that comes with the apache webserver installation. To create a password file called “my-passwords” with a user “dyndnsuser”, use this command on the shell command line:

htpasswd -c my-passwords dyndnsuser

It will let you type a password.

Now only clients with the correct username/password authentication will be able to call the dnydns.php script. Such as your DSL router.

Then, the script itself, here called dyndns.php:

<?php
# this include file contains some helper functions: 
include("dns-functions.inc");    

if ( readIpFromFile() == getIpFromRequest()) {
  header("HTTP/1.1 304 IP address didn't change", true, 304);
  message("no change in IP, address is " . getIpFromRequest() . "\\n");
} else {
  if (writeIpToFile()){
    header("HTTP/1.0 200 OK", true, 200);
    message("change successful, new address is " . getIpFromRequest() . "\\n");
  } else {
    header("HTTP/1.0  500 Error writing file or similar", true, 500);
    message("error writing file or other.\\n");
    exit;
  }
}
?>

The dns-functions.inc include file with the helper functions:

<?php
# name of the file where the IP address will be stored: 
$ip_file = "./ip";

# write debug stuff to a logfile
function message($text) {
  $file = fopen("dyndns.log", "a");
  fwrite($file, $text);
  fclose($file);
}

# get IP address from request parameter, or if none is given, 
# from the REMOTE_ADDR parameter
function getIpFromRequest() {
  # just in case the IP _is_ in the request parameters, you never know: 
  if (isset($_GET["ip"])) {
    return $_GET["ip"];
  } 
  if (isset($_POST["ip"])) {
    return $_POST["ip"];
  } 
  # if it's not, we get it from the remote address parameter: 
  return $_SERVER["REMOTE_ADDR"];
}

# read the IP address from the file. 
function readIpFromFile() {
  global $ip_file;
  if(file_exists($ip_file)) {
    if(filesize($ip_file)>0) {
      $file = fopen($ip_file, "r");
      $ip = fread($file, filesize($ip_file));
      fclose($file);
      return $ip;
    }
  }
}

# write the IP to the file. will return 'false' if there is a problem. 
function writeIpToFile()     {
  global $ip_file;
  $file = fopen($ip_file, "w");
  $result = fwrite($file, getIpFromRequest());
  fclose($file);
  return result;
}
?>

This PHP stuff was inspired by Michi’s dyndns script.

You may want to add the following to your .htaccess file to prevent people from spying out your setup:

<Files "dns-functions.inc">
  deny from all
</Files>

<Files "ip">
  deny from all
</Files>

<Files "my-passwords">
  deny from all
</Files>

All the stuff you have written so far can be placed anywhere on your webspace, as long as you specify the complete URL path to the dyndns.php script as the “update URL” in the Fritzbox Dyndns configuration.

So now the FritzBox can pass it’s IP address to your webserver. In my FritzBox’s log file, there was no message if this process was successful, I only saw log entries if something went wrong (in which case I had to look at the dyndns.log file the script writes on the webserver to check what happened).

But what then? How do we set up the webserver to actually direct requests to the FritzBox’s IP? You will need a directory and/or a subdomain on your server which to use for the redirect, so you can type something like fritzbox.mywebserver.com or myserver.com/fritzbox to be redirected to your FritzBox.

There are several possibilites to do the redirect.

1. Return a “Location” header informing the client that the document he’s looking for is somewhere else.
to do this, you’ll have to create an index.php script in the directory or at the subdomain that looks like this:

<?php
# specify complete path to the include file if it's in another directory: 
include("dns-functions.inc");    
$newurl= "http://" . readIpFromFile();
header("Location: $newurl");
?>

Whenever a client goes to fritzbox.mywebserver.com, it will be redirected to your FritzBox’s current IP address (URL in the browser’s location bar will change).

2. Use a frame redirect. This is a little bit more subtle since the IP address will not show up in the browser’s location bar, but on the other hand, if the client navigates within the page, the browser’s location bar will not show that, either. It will always just show the main domain: fritzbox.mywebserver.com

In this case, the index.php needs to look like this:

<?php
include("dns-functions.inc");    
$newurl="http://" . readIpFromFile();
?>
<html>
<frameset rows="100%">
  <frame src="<? echo $newurl; ?>">
</frameset>
<noframes>
  <body>Please follow <a href="<?echo $newurl; ?>">link</a>!</body>
</noframes>
</html>

3. Use Rewrite Rules. This will make the forwarding look most professional as the client/browser will only see the main domain and the redirect will happen on the webserver (like with a proxy). Drawback is that POST parameters are not passed on.

For the Rewrite Rules to work, you’ll have to adapt the dyndns.php script to make a change to the .htaccess file whenever a new IP is set. I haven’t tried this yet but I’ll let you know as soon as I figured it out :-)

]]>
2
schlumpf http:// <![CDATA[Cups and duplex banner pages]]> http://amazing-development.com/?p=511 2011-07-25T23:43:09Z 2011-07-25T23:43:09Z After a system update on my computer (Gentoo linux), CUPS (version 1.4.6-r2) suddenly decided it was a good idea to print the banner pages before each printing job double-sided, i.e. with the banner text on both sides of the page. This is annoying because it ruins the usefulness of the blank side of the banner page as scribbling paper.

So how to get rid of this?

There is no simple solution like checking a box somewhere in the CUPS settings. But there is an easy solution nonetheless: you can define your own banner page as described in the CUPS FAQ.

Mine is now a plain text file and looks like this:



           Job Name: {?job-name}

          From User: {?job-originating-user-name}

       Printer Name: {?printer-name}

      Creation Time: {?time-at-creation}

You can also use a postscript file, though I haven’t tried how the {?parameter} thingies work there.

This file needs to go into /usr/share/cups/banner/, then you need to restart the cups-demon, and then you can select the new banner page in the CUPS settings at http://localhost:631, go to Printers->Administration->Default Settings->Banner.

This is then printed single-sided as it should be.

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[New project at Google.]]> http://amazing-development.com/?p=495 2011-04-13T18:04:40Z 2011-04-13T18:04:40Z For the last 3 years I was working in an area called Engineering Productivity at Google. This group builds internal tools for other engineers but sadly there is very little I can talk about, except for the obvious facts: it’s challenging, fun and at a scale I hadn’t seen before.

Well, a few weeks ago I switched projects and focus areas. Now for the first time in my career[1] at Google I work on a customer facing product: the Google Privacy dashboard. Obviously I can’t write about upcoming launches, but I can assure you that there are many cool new features in the making.

[1] This is not 100% true. A little over two years ago I did some 20% work on the dashboard, so there is already a tiny little bit of code online that yours truly wrote. In case you are curious, it’s the logic that makes sure you get to see the privacy statement for your country, e.g. http://www.google.com/chrome/intl/en/privacy.html or http://www.blogger.com/privacy?hl=en.

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[Quickly convert images with ImageMagick]]> http://amazing-development.com/?p=479 2011-03-20T11:22:25Z 2011-03-20T11:22:25Z I just spent a few minutes looking for a tool to convert a small number of PNG files to JPG on Mac OS. It was just a few dozen files but so in the end fell back to the good old command line because I don’t like graphical interfaces for these tasks.

For future reference:
for file in *.png; do jpg="$(basename \\"$file\\" .png).jpg"; convert "$file" "$jpg"; done

Yes, it’s that simple once you have installed ImageMagick.

]]>
3
Frank Spychalski http://amazing-development.com <![CDATA[Customize Chrome Omnibar]]> http://amazing-development.com/?p=453 2010-09-14T11:02:41Z 2010-05-20T06:31:13Z I love Chrome! But one thing drove me crazy: we have numerous internal web-apps running on different hosts. From Firefox was easy to get to these tools by just typing the toolname (not the FQDN) in the url bar. Chrome knows it better and assumes that I want to search for toolname. A moment later it asked me if I meant toolname/ and remebers this choice (at least it annoys me only once per tool).

Yesterday I I got an idea and tryed a snippet of javascript as a search engine and was happy to see that it worked as intended. A few minutes of javascript hacking later the omnibar behaved the way I like it:

  • multiple words are a search query
  • single words are URLs, even without the trailing slash, always!
  • fix broken http at the start of an URL (happens to me sometimes when I cut’n'paste)

This is the current version of my default search engine, feel free to criticize, I’m a JS noob:
javascript:query="%s"; if (query.search(/\\s/) != -1) { window.location="http://www.google.com/search?q=" + query; } else { query = query.replace(/^h?t?t?p?:\/\//, ""); window.location="http://"+query; }

Enjoy and let me know if you have other ideas on how to improve this!

]]>
2
Frank Spychalski http://amazing-development.com <![CDATA[Dear Garmin…]]> http://amazing-development.com/?p=444 2010-02-28T09:23:05Z 2010-02-28T09:22:29Z Dear Garmin,

I don’t know if anyone told you that you build pretty good hardware but your firmware SUCKS! It’s Nearly March 2010 and your customers still have to fight with an embarrassing Y2010 bug in your code, the UI is horrible and getting anything on or off the device is a pain. I am the not-so-proud owner of an eTrex HCx but I can’t remember the last time I actually used it. Why? Because a Nexus One with My Tracks is way better than anything you have to offer. No need to jump through hoops to get tracks off the device, no proprietary drivers that work with one and a half operating systems and I carry it anyway…

Do you want me back as a customer?

Build the eTrex Android.

Specs:

  • rugged version of the Nexus One (same screen, processor, etc.)
  • replaceable batteries (with a tiny battery in the device to let me switch batteries without shutting it down)
  • better GPS antenna & chips
  • tiny solar panel on the back

If you or any other company would build a device like that, you could ask ANY price and I would buy it. So please build it. Please…

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[Buzz…]]> http://amazing-development.com/?p=439 2010-02-10T14:35:16Z 2010-02-10T14:35:16Z This is mainly a test to see how adding this blog to my Buzz account works. Additionally I want to say how thrilled I am to finally buzz with people outside work ;-)

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[JRuby on appengine with Ubuntu]]> http://amazing-development.com/?p=426 2010-01-06T14:38:56Z 2010-01-06T11:09:20Z Overall the instructions on code.google.com are really good, so this is just a dump to remind me of the Ubuntu specific stuff I did on a pristine Ubuntu installation…


sudo apt-get install sun-java6-jdk
sudo apt-get install ruby-full rubygems

This installs only rubygems 1.3.1 but appengine needs 1.3.5.

ERROR: Error installing google-appengine:
bundler requires RubyGems version >= 1.3.5


sudo gem install rubygems-update
sudo /var/lib/gems/1.8/bin/update_rubygems
sudo gem install google-appengine

Et voilà! Ten minutes later I have a running hello world in the cloud ;-)

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[pgrep or pkill sometimes do not find process]]> http://amazing-development.com/?p=415 2009-10-15T10:07:33Z 2009-10-15T10:07:33Z The usual problem: you want to kill a process.

$ ps aux | grep randomprocessname
psycho 20429 0.0 0.0 11824 1580 pts/7 S+ 12:00 0:00 /bin/bash ./randomprocessname
psycho 20528 0.0 0.0 4188 740 pts/17 R+ 12:00 0:00 grep randomprocessname
$ pkill -9 randomprocessname
WTF? Kill that sucker!
$ pkill -9 randomprocessname
No typo... Am I crazy?
$ pgrep randomprocessname
What's wrong?
$ pgrep randomprocessna
20429
$ pkill -9 randomprocessna

For some arcane reason pgrep/pkill matches only the first 15 characters.

]]>
2
Frank Spychalski http://amazing-development.com <![CDATA[Android scripting environment supports JRuby]]> http://amazing-development.com/?p=396 2009-08-04T06:46:34Z 2009-08-04T06:21:19Z Quite some time has passed since the last time I wrote something on this blog, mostly because I did little non-work related worth mentioning.

But last week I helped Damon to finally support JRuby on his Android Scripting Environment (ASE).

  • Downloaded JRuby sources (jruby-1.2.0RC1) from http://www.jruby.org/download.
  • Patched the build.xml to not include doc/index.html from dynalang.jar otherwise dx will complain about an HTML page in the ruby-complete.jar.

    $ diff -r jruby-1.2.0RC1 jruby-1.2.0RC1.patched/
    Only in jruby-1.2.0RC1.patched/: build
    diff -r jruby-1.2.0RC1/build.xml jruby-1.2.0RC1.patched/build.xml
    42c42
    <
    ---
    >
    238c238,240
    <         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar"/>
    ---
    >         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar">
    >           <exclude name="**/doc/index.html"/>
    >         </zipfileset>
    268c270,272
    <         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar"/>
    ---
    >         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar">
    >           <exclude name="**/doc/index.html"/>
    >         </zipfileset>
    387c391,393
    <         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar"/>
    ---
    >         <zipfileset src="${build.lib.dir}/dynalang-0.3.jar">
    >           <exclude name="**/doc/index.html"/>
    >         </zipfileset>
  • Downloaded the JSON sources from http://rubyforge.org/frs/?group_id=953 and put them in lib/ruby/1.8/json/
  • Copied android.rb to lib/ruby/1.8/.
  • Built jar-complete (ant jar-complete) and added jruby-complete.jar to eclipse project.
  • Connected the bits and pieces in com.google.ase.interpreter.jruby

    But beware! The new ASE apk is HUGE (4.6M) and JRuby is fairly slow. But it works ;-)


]]>
7
Frank Spychalski http://amazing-development.com <![CDATA[Android G1 gprs setup for simyo with 1gb data plan]]> http://amazing-development.com/?p=382 2010-01-05T13:38:57Z 2009-03-03T08:52:38Z Last week I added the 1gb data plan to my Simyo contract to use the SIM with my Christmas G1. I tried a few configurations I found online for simyo but they did not work for me. After some frustration I tried an eplus configuration and this one finally worked for me:


Name: does not matter
APN: internet.eplus.de
Proxy: <empty>
Port: <empty>
Username: eplus
Password: <empty>
Server: <empty>
MMSC: <empty>
MMS proxy: <empty>
MMC: 262
MNC: 03
APN type: <empty>

Update:

The recommended configuration is:


Name: does not matter
APN: internet.eplus.de
Proxy: <empty>
Port: <empty>
Username: simyo
Password: simyo
Server: <empty>
MMSC: <empty>
MMS proxy: <empty>
MMC: 262
MNC: 03
APN type: <empty>

After the initial success with the eplus configuration, I had some problems with it, too. But I guess these errors were internal eplus network hiccups because they were gone without me changing anything and now both configs work.

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[Tab + Firefox on OS X]]> http://amazing-development.com/?p=377 2009-02-26T17:19:30Z 2009-02-26T17:19:30Z A few days ago I finally upgraded my (== my employer’s) Macbook Pro from Tiger to Leopard. Everything worked flawless except for one crazy problem: I could not use to tab key the way I used to in forms. My usual routine of quickly entering username, tab, password, tab, hit return did not work anymore. I searched through the Firefox settings but couldn’t find the “Drive psycho crazy”-option.

But thanks to the omniscient internet I found a solution. At the bottom of the “Keyboard & Mouse” preferences page is a setting which allows you to navigate only between text boxes or between all controls. I don’t know who came up with the great idea that the default should be “text boxes and lists only”, but I guess the mighty Steve knows what he is doing.

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[First sighting of JRuby on Android]]> http://amazing-development.com/?p=368 2009-03-04T20:50:30Z 2009-02-24T13:25:24Z Last week I cross-compiled miniruby to Android (more details on that adventure later). I asked some time ago about JRuby on Android and it seems like Charles Nutter finally managed to run JRuby on Android.

# cat test.rb
require 'java'
import java.lang.System

class Ruboto
  def greet(who)
    puts "Hello, #{who}!"
  end
end

name = System.get_property('java.runtime.name')
Ruboto.new.greet(name)

# dalvikvm -classpath ruboto.jar org.jruby.Main -X-C test.rb
Hello, Android Runtime!

This leaves only one question: where can I get ruboto.jar to start playing around with it :-)

]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[Code reviews work]]> http://amazing-development.com/?p=354 2009-02-22T20:30:05Z 2009-02-22T20:24:50Z Seems like nowadays I only write blog posts in response to other blog articles… But I hope this is better than not writing at all. Ted Neward asks if code reviews do actually work because evidence suggests, that the scientific review process does not.

Only 8% members of the Scientific Research Society agreed that “peer review works well as it is”. (Chubin and Hackett, 1990; p.192).

“A recent U.S. Supreme Court decision and an analysis of the peer review system substantiate complaints about this fundamental aspect of scientific research.” (Horrobin, 2001)

Horrobin concludes that peer review “is a non-validated charade whose processes generate results little better than does chance.” (Horrobin, 2001). This has been statistically proven and reported by an increasing number of journal editors.

But, “Peer Review is one of the sacred pillars of the scientific edifice” (Goodstein, 2000), it is a necessary condition in quality assurance for Scientific/Engineering publications, and “Peer Review is central to the organization of modern science…why not apply scientific [and engineering] methods to the peer review process” (Horrobin, 2001).

Chubin, D. R. and Hackett E. J., 1990, Peerless Science, Peer Review and U.S. Science Policy; New York, State University of New York Press.

Horrobin, D., 2001, “Something Rotten at the Core of Science?” Trends in Pharmacological Sciences, Vol. 22, No. 2, February 2001. Also at http://www.whale.to/vaccine/sci.html and http://post.queensu.ca/~forsdyke/peerrev4.htm (both pages were accessed on February 1, 2009)

Goodstein, D., 2000, “How Science Works”, U.S. Federal Judiciary Reference Manual on Evidence, pp. 66-72 (referenced in Hoorobin, 2000)

This sounds so true to me. I heard way more than one story from Ph.D. students where the professors who were supposed to review papers just forwarded this work to their slaves.

But the fact that academic peer reviews are possibly a failure should not be used to argue against the usefulness of code reviews.

As a reviewee
My first project at Google was written in Python. I had never written a single line of Python before. The feedback I got from the first few reviews helped me learn a lot about Python in a very short time. I don’t know how many times I got these “Yes, this would work but if you replace these 6 lines with that short statement it would work, too” comments.

The same is true for my second project in C++. I have written some C++ before but this was for my Diploma thesis and the code was far from professional. Again the feedback I got from my reviewers helped me learn C++ a lot faster than I would have otherwise. This project was a lot bigger than the first one so I got a few “we already have this implemented here” comments.

As a reviewer
I usually don’t pay that much attention to our coding conventions but my pet peeve is looking out for missing comments. Every time I have to think about a single line of code, this is a pretty good signal that a comment is missing.

Reviewing other people’s code makes sure that I have at least a little knowledge about the areas of our project I do not work on directly.

Conclusion
Code reviews take time and need the proper tool support. But if it is done right, they are useful and help in a couple of different areas:

  • Learning a new language or about new piece of code.
  • Avoiding code duplication.
  • Making sure code is documented.
  • Distribute the knowledge.
]]>
0
Frank Spychalski http://amazing-development.com <![CDATA[Pair Programming killed the Uber-coder…]]> http://amazing-development.com/?p=347 2009-02-21T21:08:45Z 2009-02-21T20:49:22Z I stumbled over a great essay on pair-programming (via James Shore).

Rod Hilton explains it much better than I could ever do, why you should Pair-Program. But I haven’t heard about the “ping-pong pairing”:

When doing Test-Driven Development, one of the things we do is called “ping-pong pairing”. So the other developer will write a test, then make it compile but fail. Then he passes the keyboard to me. I implement the feature just enough to make the test pass, then I write another failing test and pass it back.

I have to try this on Monday…

]]>
2