no symbol version for module_layout

15. November, 2009

This one drove me nuts. After upgrading to openSUSE 11.2, I couldn’t compile the NVIDIA (warning: Big flash welcome) or the VirtualBox drivers. Well, the compilation was working but loading failed with:

no symbol version for module_layout

This post finally pointed me in the right direction. To fix the issue, just run zypper in kernel-default-devel as root (or kernel-desktop-devel if you use the desktop kernel).


Goodbye Fallout 3

11. November, 2009

I made a mistake. A big mistake. I admit it. I shouldn’t have. I still did. I bought the game officially in a store. Sorry. Won’t happen again. Bethesda is now on my “Don’t Buy” list and Sony is close.

What happened. A year ago, I bought Fallout 3 in a shop. It’s a German uncut version. I’d actually preferred the cut version; the splatter effect is probably some nice piece of FX code but blood doesn’t give me much. Can’t have that. I’m in Switzerland and I can’t do as I please. On top of that, it seems my shop sold me the Austrian version. It’s German, too, but different. Somehow. I don’t know. I’m just a stupid gamer. The main difference is that when I buy the addons in Sony’s PSN, then I get something that doesn’t work with my game. Because I must have the Swiss version. Since I’m in Switzerland. And I bought the game in Switzerland. And I have a Swiss PSN account. I think. I don’t know. I’m just a stupid gamer.

So what happens is that I have an illegal copy of the game. Illegal as in “if you’re in Switzerland”. Why Bethesda decided to produce three German versions? I don’t know. I’m just a stupid gamer. I don’t need to know such things. Why were the DLCs available for months for Xbox but not for PS3? I don’t know. Why did everyone say that the DLCs would never come to the PS3? I don’t know. Maybe it was because Bethesda knew what would happen. Or maybe Sony treats them like their customers. I don’t know.

The net result is that I have a game which I can’t upgrade (at least not without illegally creating an Austrian account on PSN). I probably can’t buy the GOTY Editition without loosing my save games. I don’t know for sure. I’m not sure I care anymore. My blood pressure raises when I only see the game box. I buy games to relax, not to heap more problems on my plate. I don’t care who is responsible for this crap. I don’t understand why it’s more cheap for Sony to put some text in the game description (“Don’t buy this unless you have BLES-00399”) instead of checking the list of installed games. It’s also sad that Switzerland doesn’t have any laws to protect customers who buy over the Internet. Sony can put anything in the rules of the PSN and I can only weep. I can’t even sell or ebay things I buy on PSN.

Makes me wonder what happens should I ever have to move back to Germany. Will I have to buy all my games again? Or will Sony be nice and allow me to keep my Swiss PSN account even though I’ll lose my Swiss credit card? Maybe they’ll expect me to live close to the border, so I can still buy games. Or carry the PS3 over, hook it up to PSN via my mobile phone, so I can update the games I bought.

Some more frustration: Fallout 3 has left about 600 save games on my harddisk. It would take me approx. 24 hours to delete them (it’s a process that involves pressing eight buttons in the correct sequence).

Or how about this: I bought a Sony LCD TV because the PS3 can talk to my media server. I was naively assuming that the TV would work just like the console. Well, it doesn’t. I can watch photos and videos on my PS3 but not directly on the TV.

Well done. For some reason, Xbox and Wii sell better than the PS3. I wonder why. The PS3 looks so much better!


Software Design With Modern Languages

6. November, 2009

There is a nice series of articles on IBM’s developerworks by Neal Ford which talks about software design and how modern languages help to come up with a clear and cost-efficient design. To get a grasp why this is important, I like this quote:

Building software isn’t like digging a ditch. If you make compromises when you dig a ditch, you just get uneven width or unequal depth. Today’s flawed ditch doesn’t prevent you from digging a good ditch tomorrow. But the software you build today is the foundation for what you build tomorrow. Compromises made now for the sake of expediency cause entropy to build up in your software. In the book The Pragmatic Programmer, Andy Hunt and Dave Thomas talk about entropy in software and why it has such a detrimental effect (…). Entropy is a measure of complexity, and if you add complexity now because of a just-in-time solution to a problem, you must pay some price for that for the remaining life of the project.

Any software developer should be familiar with the concept of entropy and how it affects their lives.

In a later installment, Neal shows some reasons how modern languages allow to implement many of the design patterns by the GoF much more naturally with Groovy.


What’s Your Mission?

2. November, 2009

There is another nice article from Joel Spolsky: Figuring out what your company is all about. It’s all about

“We help $TYPE_OF_PERSON be awesome at $THING”

So what do you work on and how does it help your customers to be awesome with something? If you can’t answer this simple question, then you should sit down and ponder why not. It will help you to achieve your goals.

There is one point about the article, though. Joel says: “We help the world’s best developers make better software.” Uh … only the best? How about the vast majority, the good ones?


If You Ever Need To Design a Standard

2. November, 2009

… then keep it simple.

If you need a reason for this (other than plain old common sense), see this blog post.


Google Translate Just Got Better

16. October, 2009

You remember the old Google Translate? You know, the one which was really good for a moment of fun when you were bored: Just give it some text and laugh at the result. You could try to translate some text back and forth several times.

Well, it just got better. A lot better. Here is an example:

Der Hobby-Baumarkt war wie leer gefegt. Einkaufswagen an den Kassen, wild durcheinander, die Panik deutlich spürbar, in der Zeit eingefroren. Einkäufe, nur gesammelt oder schon bezahlt, lagen verstreut auf dem Boden. Werbebildschirme an der Decke blinkten rot, forderten alle Besucher zum sofortigen Verlassen des Gebäudes auf. Der modern geschwungene Informationsstand war verlassen, die Kassen eingeschaltet aber geschlossen. Hier war niemand mehr.

When I run this through GT, I now get something that is very readable, almost correct:

The Hobby-DIY was as completely empty. Shopping Cart at the tills, in wild confusion, the panic palpable, frozen in time. Purchases, only collected or already paid, lay scattered on the floor. Advertising screens on the ceiling flashed red, urged all attendees to immediately leave the building. The modern, curved level of information was left switched on but the cash is closed. There was no one.


20K on SO

16. October, 2009

Finally, 20’001 points on SO 🙂


PyScan 0.6

10. October, 2009

I’ve done some more work on PyScan. Most work is under the hood but I’ve got a system to load and save projects and a bug was fixed: If you pressed the Scan button too quickly with 0.4, the last image could have been overwritten. PyScan is now based on hplip 3.9.8.

PyScan 0.6.tar.gz (16KB, MD5 Sum: 2b5e23099be438ceceb69ec23d64cec6)

See the original post for features and the system requirements.


Attaching Sources To Eclipse Artifacts

8. October, 2009

After running mvn eclipse:make-artifacts -DstripQualifier=true -DeclipseDir=...path/to/eclipse, you might have lots of source JARs but they aren’t in the right place for Maven 2 to pick them up.

This little Python script fixes that. Just run it after eclipse:make-artifacts.

Note: You may be wondering why I use eclipse:make-artifacts instead of the recommended eclipse:to-maven. Simple: I don’t like to have a dozen core-*.jar in my project.

"""Maven 2 Eclipse Artifact Source resolver

After importing Eclipse artifacts into an M2 repository with

> mvn eclipse:make-artifacts -DstripQualifier=true -DeclipseDir=.../eclipse

run this script to move all source JARs in the right place for
Maven 2 to pick them up.
"""
import os, sys
from shutil import copyfile

def processGroup(path):
    print 'group %s' % path
    for dir in os.listdir(path):
        path2 = os.path.join(path, dir)
        if '.' in dir:
            processArtifact(path2)
        else:
            processGroup(path2)

def processArtifact(path):
    srcPath = path + '.source'
    #print 'processArtifact',srcPath
    if os.path.exists(srcPath):
        processArtifactWithSource(path)

def processArtifactWithSource(basePath):
    binPath = basePath
    srcPath = basePath + '.source'
    baseName = os.path.basename(basePath)
    print 'artifact', baseName
    
    for version in os.listdir(binPath):
        vbinPath = os.path.join(binPath, version)
        vsrcPath = os.path.join(srcPath, version)
        
        srcJar = os.path.join(vsrcPath, '%s.source-%s.jar' % (baseName, version))
        destJar = os.path.join(vbinPath, '%s-%s-sources.jar' % (baseName, version))
        
        if os.path.exists(srcJar):
            print '%s -> %s' % (srcJar, destJar)
            copyfile(srcJar, destJar)

m2repo = os.environ['M2_REPO']
if not m2repo:
    raise Exception('Env variable M2_REPO is not set')

root = os.path.join(m2repo, 'org', 'eclipse')
for dir in os.listdir(root):
    path = os.path.join(root, dir)
    processGroup(path)

Updated 01. December 2009: The script now figures out where your M2 repo is. Plus a minimum of documentation.


Testing the Impossible: Shifting IDs

7. October, 2009

So you couldn’t resist and wrote tests against the database. With Hibernate, no less. And maybe some Spring. And to make things more simple, toString() contains the IDs of the objects in the database. Now, you want to check the results of queries and other operations using my advice how to verify several values at once.

During your first run, your DB returns some values, you copy them into a String. When you run the tests again, the tests fail because all the IDs have changed. Bummer. Now what? Just verify that the correct number of results was returned?

Don’t. assertEquals (3, list.size()) doesn’t give you a clue where to search for the error when the assert fails.

The solution is to give each ID a name. The name always stays the same and when you look at the test results, you’ll immediately know that the ID has been “fixated” (unlike when you’d replace the number with another, for example). Here is the code:

import java.util.*;

/** Helper class to fixate changing object IDs */
public class IDFixer
{
    private Map<String, String> replacements = new HashMap<String, String> ();
    private Map<String, Exception> usedNames = new HashMap<String, Exception> ();
    
    /** Assign a name to the number which follows the string <code>id=</code> in the object's
     * <code>toString()</code> method. */
    public void register (Object o, String name)
    {
        if (o == null)
            return;
        
        String s = o.toString ();
        int pos = s.indexOf ("id=");
        if (pos != -1)
            pos = s.indexOf (',', pos);
        if (pos == -1)
            throw new RuntimeException ("Can't find 'id=' in " + s);
        String search = s.substring (0, pos + 1);
        
        pos = search.lastIndexOf ('=');
        String replace = search.substring (0, pos + 1) + name + ",";
        
        add (name, search, replace);
        
        registerSpecial (o, name);
    }

    /** Add a search&replace pattern. */
    protected void add (String name, String search, String replace)
    {
        if (usedNames.containsKey (replace))
            throw new RuntimeException ("Name "+name+" is already in use", usedNames.get (replace));
        
        //System.out.println ("+++ ["+search+"] -> ["+replace+"]");
        usedNames.put (replace, new Exception ("Name was registered here"));
        replacements.put (search, replace);
    }
    
    /** Allow for special mappings */
    protected void registerSpecial (Object o, String name)
    {
        // NOP
    }

    /** Turn a <code>Collection</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Collection c)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Object o: c)
        {
            buffer.append (delim);
            delim = "\n";
            buffer.append (o);
        }
        return toString (buffer);
    }
    
    /** Turn a <code>Map</code> into a <code>String</code>, replacing all IDs with names. */
    public String toString (Map m)
    {
        StringBuilder buffer = new StringBuilder (10240);
        String delim = "";
        for (Iterator iter=m.entrySet ().iterator (); iter.hasNext (); )
        {
            Map.Entry e = (Map.Entry)iter.next ();
            
            buffer.append (delim);
            delim = "\n";
            buffer.append (e.getKey ());
            buffer.append ('=');
            buffer.append (e.getValue ());
        }
        return toString (buffer);
    }
    
    /** Turn an <code>Object</code> to a <code>String</code>, replacing all IDs with names.
     * 
     *  <p>If the object is a <code>Collection</code> or a <code>Map</code>, the special collection handling methods will be called. */
    public String toString (Object o)
    {
        if (o instanceof Collection)
        {
            return toString ((Collection)o);
        }
        else if (o instanceof Map)
        {
            return toString ((Map)o);
        }
        
        if (o == null)
            return "null";

        String s = o.toString ();
        for (Map.Entry<String, String> entry: replacements.entrySet ())
        {
            s = s.replace (entry.getKey (), entry.getValue ());
        }
        
        return s;
    }

    public boolean knowsAbout (String key)
    {
        return replacements.containsKey (key);
    }
}

To use the IDFixer, call register(object, name) to assign a name to whatever follows the number after id=. The method will search for this substring in the result of calling object.toString().

If you call IDFixer.toString(object) for a single object or a collection, it will replace id=number with id=name everywhere. If you have references between objects, you can register additional pattern to be replaced by overriding registerSpecial().

To make it easier to compare lists and maps in assertEquals(), each item gets its own line.