Tuesday, December 9, 2008

VMWare and key maps on Fedora 10

I am one of those poor souls who require Windows for some tasks. I wish, this weren't the case, but that's how things are. Fortunately, VMware Server comes to my rescue: A comparatively small VM of about 15GB is sufficient.

However, after my recent upgrade to Fedora 10, I suddenly noticed that some of my keys were no longer working in the VM: This included the arrow keys, but also some keys, which have been reachable via "Alt Gr" only, like '@', or '\'.

A quick search on Google revealed that I am far from being the only one bothered with that problem. In fact, the difficulties are more likely related to a Kernel upgrade. Users of Ubuntu 8.10 with a similar new kernel seem to suffer the same trouble. Fortunately, solutions were always at hand. For example, the VMware support suggests to use

xkeymap.usekeycodeMapIfXFree86 = true


xkeymap.usekeycodeMap = true

in ~/.vmware/config. Other sources proposed a list of explicit code settings instead, like

xkeymap.keycode.108 = 0x138 # Alt_R
xkeymap.keycode.106 = 0x135 # KP_Divide
xkeymap.keycode.104 = 0x11c # KP_Enter
xkeymap.keycode.111 = 0x148 # Up
xkeymap.keycode.116 = 0x150 # Down
xkeymap.keycode.113 = 0x14b # Left
xkeymap.keycode.114 = 0x14d # Right
xkeymap.keycode.105 = 0x11d # Control_R
xkeymap.keycode.118 = 0x152 # Insert
xkeymap.keycode.119 = 0x153 # Delete
xkeymap.keycode.110 = 0x147 # Home
xkeymap.keycode.115 = 0x14f # End
xkeymap.keycode.112 = 0x149 # Prior
xkeymap.keycode.117 = 0x151 # Next
xkeymap.keycode.78 = 0x46 # Scroll_Lock
xkeymap.keycode.127 = 0x100 # Pause
xkeymap.keycode.133 = 0x15b # Meta_L
xkeymap.keycode.134 = 0x15c # Meta_R
xkeymap.keycode.135 = 0x15d # Menu

In my case, none of these solutions worked completely. The hex codes were refused by VMware Server and the usekeycodemap stuff was fixing some keys, but not all. It turned out, that I had to convert the hex key codes to decimal in order to get it working. For the convenience of those who share my trouble, I post my settings here:

xkeymap.keycode.108 = 312 # Alt_R
xkeymap.keycode.106 = 309 # KP_Divide
xkeymap.keycode.104 = 284 # KP_Enter
xkeymap.keycode.111 = 328 # Up
xkeymap.keycode.116 = 336 # Down
xkeymap.keycode.113 = 331 # Left
xkeymap.keycode.114 = 333 # Right
xkeymap.keycode.105 = 285 # Control_R
xkeymap.keycode.118 = 338 # Insert
xkeymap.keycode.119 = 339 # Delete
xkeymap.keycode.110 = 327 # Home
xkeymap.keycode.115 = 335 # End
xkeymap.keycode.112 = 329 # Prior
xkeymap.keycode.117 = 337 # Next
xkeymap.keycode.78 = 70 # Scroll_Lock
xkeymap.keycode.127 = 256 # Pause
xkeymap.keycode.133 = 347 # Meta_L
xkeymap.keycode.134 = 348 # Meta_R
xkeymap.keycode.135 = 349 # Menu

Thursday, November 27, 2008

Fedora PreUpgrade

You've got to learn every day, that's for sure. Sometimes it's tedious (yes, I admit to being so old as no longer love learning for its own sake), but sometimes it's nice. An example of the latter was today when I read about Fedora PreUpgrade.

A while ago, I wrote about UNetbootin, a very nice utility for starting Linux installation or live cd's like GPartEd from a running system without burning a CD or DVD. I've been using it since then and will do so in the future. However, in the case of Fedora, CentOS or Red Hat Enterprise Linux upgrades, using UNetbootin has its limitation. (Which is, of course, not the fault of UNetbootin, but a limitation of these distributions, in the latter simply referenced as Fedora.)

For upgrade of Fedora, there are basically two options:

  1. The officially supported way is using the Anaconda Installer. That means booting from a CD, DVD (or via UNetbootin :-). That's nice, if you have a DVD ready and also have direct access to the system.
  2. The officially unsupported way is using Yum. Much better in some aspects: You can do it from within a running system, even remotely. However, if you are reading the Yum Upgrade FAQ, then you cannot fail to notice that there seem to be a lot of traps and possible problems. I have had my share of these in the past, for example when the IDE devices have been renamed from /dev/hdx to /dev/sdx: At that point you could bet your house on Yum upgrades failing.
    For similar reasons, the Yum upgrade should not happen with a UI running. If you are actually using the machine (for example on your own desktop), then that means a lot of more downtime than you want. (Ok, you might use the download-only plugin... I am ranting, but there's a reason for this Blog's title...)

In other words, you either got to do it safe (Anaconda) or nice (Yum). No longer: Use PreUpgrade.

It's a small python utility with a UI. You start it. It connects to the Fedora server and gathers a list of releases, which are available for an upgrade. (This might include alpha and/or beta releases, if you like.) Proxy servers are supported, btw., I did not check, whether it uses http_proxy, yum.conf, or both. You select a release and click on "Apply". After that, it automatically calculates dependencies and loads the required RPM's to your hard drive. So far, you' ve got the advantages and the comfort of Yum.

But that's not all. In the next step, PreUpgrade is loading a kernel image, a ram disk and similar stuff to your hard drive and configures your system for using them at the next reboot: In other words, it does what UNetbootin would, for you.

All this is happening in the background. You can continue your work, no downtime so far. Finally, when PreUpgrade is finished, you may click on Reboot: Anaconda comes up, performing a safe installation without downloading anything or using a slow CDROM drive.

By using PreUpgrade, I have just upgraded from Fedora 9 to Fedora 10. My downtime was about 10 minutes. (Excellent possibility to get some coffee!) Aint this magic?

Sunday, November 16, 2008

Authors to remember: Robert Sheckley

In the last years, I tend to find that books I'd like to obtain are out of print or generally unavailable. No surprise: With 45, you and the time of your youth definitely tend to become part of the scrap heap. In some cases, thats simply sad, because these authors did an excellent job at their time and absolutely deserve being read nowadays. We should not forget them. Don't know whether I'll be able to start a series here, but let's begin with Robert Sheckley.

Robert Sheckley had his best times in the fifties, when he wrote, in particular, a series of short story collections. These short stories were usually published in science fiction magazines. When reading his books, you'll find everything that makes science fiction: Space ships, as much as you like, roboters of all kinds, and so on. (I don't want to reduce him to that: He wrote detective stories, fantasy, or adventure stories as well as scripts for TV and films, but let's forget about that today.) At that time, SF was quite popular.

But his stories were different: Witty, surprising, absurd, and sometime hilariously funny. They were not so much about predictions, but about humans and how they are. For example, in "A ticket to Tranai" a young man receives the chance of his live: He's able to rule a world, an Utopia. Surprisingly, things are not as they should be and in the end he hardly manages to escape back to earth. The story ends with him visiting the same bar where the story began, void of all illusions and proposing Tranai to others. Why should they be better off than him?

In "Bad Medicine" a man is "cured" by a mechanic psychiatrist. It turns out, the "mechanotherapist" wasn't made for human minds, but martians. Of course, the result is not the expected.

An almost prophetic example is "Price of Peril": A TV show proposes a high sum of money to a man, if he can manage to survive the next week. His opponents are a bunch of (also hired) criminals. Of course, the TV cameras are watching him 24 hours a day. And of course, they have their own view how the game should end...

To quote Douglas Adams (from the cover of Hunter/Victim): "I had no idea the competition was so terrifyingly good."

Sunday, October 5, 2008

How to skip a test with JUnit

In my past, about 10 years, ago, I've been a very committed Perl programmer. This meant, in particular, that I have written my share of test cases. One of the nicer features of the testing framework is the possibility to skip tests at runtime. One can use this for all kind of things like suppressing slower tests in every days work, for restricting tests to certain operating systems, and so on. The test suite runner recognizes skipped tests and reports them as skipped (as opposed to failed, or successfull).

On the other hand, I have always missed this feature in JUnit. But, from what I can tell, it's finally there in JUnit 4.4. Suggest the following example, which runs a test only, if the system property "run.slow.tests" is set.

import static org.junit.Assume.*;

public class ConditionalTest {
@Test fastTest() { // Performed always

@Test slowTest() { // Performed always

Internally, the feature works by throwing an AssumptionViolationException.

Thursday, July 31, 2008

Announce: Apache JaxMas

I'm glad to announce that the world has got yet another (tiny!) open source project, Apache JaxMas. JaxMas is basically a very, very incomplete JAXR implementation.

Why another JAXR implementation? The world has already got plenty of them, for example Apache Scout. My job is mainly about CentraSite, my employers implementation (there's more to it than a simple JAXR implementation, of course). But they all share a common problem: It is basically impossible to use them for unit testing and non-trivial, to use them within integration tests.

The problem is that they all require a running server. And, what's more, it is difficult to start from a predefined set. That's where JaxMas comes into play. JaxMas is running on HSQLDB. The embedded mode of HSQLDB is obviously ideal for unit tests. JaxMas is based on the assumption of an initial schema, that is created by the unit test. The schema is basically a set of SQL commands, but a macro processor allows to create taxononmies and other registry objects relatively easily.

Btw, you might ask me why I didn't choose Apache Derby as the engine. Well, I did initially, but DERBY-338 was blocking me. This issue prevents that two foreign key references exist between the same tables with different ON DELETE clauses. However, I need that feature for deleting even complex registry objects with a simple DELETE statement.

Friday, July 18, 2008


I am currently preparing a proposal for the Apache Labs. It will be something like an absolute minimal registry for writing JAXR based unit tests, running on Apache Derby. The idea is, that with existing registries (in my case CentraSite) you'll rarely be writing unit tests, because the overhead is so high, that nothing below integration tests makes sense.

Like any child, this one needs a name. With names, however, I am as inventive as the former federal german phone company. (The companies current successor is inventive, in particular with new ways to bill you for things that have been free in the past.) Usually, a projects name would be discussed on the corresponding mailing list.

In this case, however, it is more than likely, that I'll be the only user. So I though to ask here: As far as I know, this blog has at least two users and thats more than one, isn't it? So far my only idea was JaxMas, in honour of my former child JaxMe and due to the fact, that I had the idea on christmas. Better suggestions more than welcome!

Friday, July 4, 2008

When the human mind blocks itself

I am usually working in an office with some 40 other colleagues. The best piece of the office is (at least IMO), the espresso machine. It is one of these machines, which does everything for you by putting a cup on it and pressing a button: Coffee beans are grinded (wonderful flavour), pouring water, and finally pressing the coffee into your cup. That's it and the result has something you could actually call crema. (That is, it doesn't taste as well as in your favourite restaurant, but much better than what the traditional coffee machines gave you.) I like it and visit the place several times a day.

Of course, the "does everything" part ends for you. The water is empty? Got to refill it. No more beans? Got to fetch a bag from the rack. Other tasks include cleaning the machine, decalcify it, and the like. In german, the word user is frequently translated to "Bediener", which could as well mean servant. But that's as it is, you simply do it and are well of not to spend too many thoughts about it. (And, in particular, not writing silly blog entries like this ... :-)

The only question arising: When is the right time to perform these tasks? If you got your coffee and the machine informs you that it needs water, should you do this immediately, or leave it to the next coffee drinker?

Of course, it is polite to do it immediately. And, because it is polite, you are expected to do it. But, for whatever reason, you tend to visit the machine and find it announcing that it needs water or beans. May be, its people like me who honestly wish to be polite, but are simply too absorbed in their thoughts to notice the message when leaving. May be, there actually are (as some of us believe) undecent parasites, which leave the work to others. "It's always me" is a typical phrase near the coffee machine.

Now, let's try to consider the unimaginable: What if we all would decide to leave the job for the next one? Would it mean more work for anyone? Yes, it would, for the parasites (I personally doubt that they exist.) and for thoughtless people like me, who would actually need to do the job themselves. It would reduce the work of the honest, upright people who get never tired to encourage us not to leave the work to them. Noone would need to feel disadvantaged. It could simply work.

Of course, it doesn't. Because, at some point, someone would wish to be polite again. And then we'd have the circle. The human mind blocks itself ...

Thursday, March 6, 2008

Native libraries with the EclipseTotale Tomcat Plugin

I am a happy user of the EclipseTotale Tomcat Plugin: It is so much faster, more lightweight and convenient and generally easier to use than the server stuff of Eclipse WTP. (Of course, it cannot run JBoss, but I don't expect that it does. :-) It is one of the tools that I use every day.

Unfortunately, I am also one of the poor souls who are forced to work with native libraries. (In my case the authentication libraries of CentraSite.) Native libraries are generally a pain and one of the devils more successful inventions. In the case of the Tomcat plugin they have prevented me from using it for quite some time. Instead I was typically starting Tomcat from a shell script: The shell script has been setting the necessary environment variables like PATH under Windows or LD_LIBRARY_PATH under Linux/Unix, which indicate the location of shared libraries aka DLL's.

I wondered for some time, whether it wouldn't be quite easy to add support for setting these variables to the plugin. At some point, I contacted the plugins author and asked for the sources in order to add the missing feature myself. He never responded. However, recently I found that he did something much better: If you open the plugins zip file, then you'll notice, that it contains a file called tomcatsrc.zip. Yes, these are the plugins sources. And, even better, there also is a file called license.txt, which tells you that the plugin is licensed under the MIT license, a very liberal license, that allows me to change the sources. I could even distribute an updated plugin.

It turned out, that the work was even quite easy. Adding a control to the preferences page, some resource strings and a piece of configuration to an Eclipse configuration object for launching external programs, that's basically it.

If you're interested, I'll post the patch below. Let's hope, that this patch, which I have submitted to the plugins author will appear in the next version.

diff -ubr --exclude='*.class' ./src/com/sysdeo/eclipse/tomcat/editors/ListFieldEditor.java /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/com/sysdeo/eclipse/tomcat/editors/ListFieldEditor.java
--- ./src/com/sysdeo/eclipse/tomcat/editors/ListFieldEditor.java 2005-04-11 12:04:10.000000000 +0200
+++ /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/com/sysdeo/eclipse/tomcat/editors/ListFieldEditor.java 2008-03-06 21:14:19.000000000 +0100
@@ -6,9 +6,9 @@
import java.util.StringTokenizer;

import org.eclipse.jface.dialogs.IDialogConstants;

+import org.eclipse.jface.dialogs.IInputValidator;

import org.eclipse.jface.dialogs.InputDialog;

import org.eclipse.jface.preference.ListEditor;

-import org.eclipse.jface.util.Assert;

import org.eclipse.swt.SWT;

import org.eclipse.swt.events.DisposeEvent;

import org.eclipse.swt.events.DisposeListener;

@@ -55,10 +55,13 @@

protected SelectionListener selectionListener;

+ private final IInputValidator validator;



* Creates a new list field editor


public ListFieldEditor() {

+ validator = null;



@@ -70,10 +73,13 @@
* the label text of the field editor

* @param parent

* the parent of the field editor's control

+ * @param validator

+ * A validator for validating the users input


- public ListFieldEditor(String name, String labelText, Composite parent) {

+ public ListFieldEditor(String name, String labelText, Composite parent, IInputValidator validator) {

init(name, labelText);


+ this.validator = validator;



@@ -440,7 +446,7 @@
defaultValue = list.getSelection()[0];


- InputDialog dialog = new InputDialog(getShell(), "New Tomcat JVM paramater", "Enter a JVM parameter", defaultValue, null);

+ InputDialog dialog = new InputDialog(getShell(), "New Tomcat JVM paramater", "Enter a JVM parameter", defaultValue, validator);

String param = null;

int dialogCode = dialog.open();

if (dialogCode == InputDialog.OK) {

diff -ubr --exclude='*.class' ./src/com/sysdeo/eclipse/tomcat/TomcatBootstrap.java /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/com/sysdeo/eclipse/tomcat/TomcatBootstrap.java
--- ./src/com/sysdeo/eclipse/tomcat/TomcatBootstrap.java 2007-03-28 20:58:58.000000000 +0200
+++ /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/com/sysdeo/eclipse/tomcat/TomcatBootstrap.java 2008-03-06 20:49:44.000000000 +0100
@@ -11,8 +11,10 @@
import java.io.PrintWriter;

import java.util.ArrayList;

import java.util.Collection;

+import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

+import java.util.Map;

import org.eclipse.core.resources.IFile;

import org.eclipse.core.resources.IProject;

@@ -198,14 +200,16 @@
jvmArguments.append(" " + vmArgs[i]);


+ Map envVariables = getEnvVariables();


if(action == RUN) {

- VMLauncherUtility.runVM(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, saveConfig);

+ VMLauncherUtility.runVM(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, saveConfig, envVariables);


if(action == LOG) {

- VMLauncherUtility.log(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger);

+ VMLauncherUtility.log(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, envVariables);


if(action == ADD_LAUNCH) {

- VMLauncherUtility.createConfig(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, true);

+ VMLauncherUtility.createConfig(getLabel(), getMainClass(), classpath, bootClasspath, jvmArguments.toString(), programArguments.toString(), isDebugMode(), showInDebugger, true, envVariables);



@@ -375,4 +379,17 @@
return StringUtil.concatUniq(previous, prefBootClasspath);


+ private Map getEnvVariables() {

+ final String[] envVariables = StringUtil.cutString(TomcatLauncherPlugin.getDefault().getEnvVariables(), TomcatPluginResources.PREF_PAGE_LIST_SEPARATOR);

+ final Map result = new HashMap();

+ if (envVariables != null) {

+ for (int i = 0; i < envvariable =" envVariables[i];" key =" TomcatLauncherPlugin.getEnvVariableKey(envVariable);" value =" TomcatLauncherPlugin.getEnvVariableValue(envVariable);" exclude="'*.class'" field_width =" 50;" jvmparamaters =" new" jvmparamaters =" new" jvmclasspath =" new" jvmbootclasspath =" new" envvariables =" new" exclude="'*.class'" tomcat_pref_jvm_parameters_key = "jvmParameters" tomcat_pref_jvm_classpath_key = "jvmClasspath" tomcat_pref_jvm_bootclasspath_key = "jvmBootClasspath" tomcat_pref_env_variables_key = "envVariables" tomcat_pref_projectsincp_key = "projectsInCp" tomcat_pref_projectsinsourcepath_key = "projectsInSourcePath" tomcat_pref_computesourcepath_key = "computeSourcePath" pref =" TomcatLauncherPlugin.getDefault().getPreferenceStore();" pref =" TomcatLauncherPlugin.getDefault().getPreferenceStore();" offset =" envVariable.indexOf('="');" offset ="="" s =" envVariable.substring(0," offset =" envVariable.indexOf('="');" offset ="="" exclude="'*.class'" pref_page_parameters_label =" TomcatLauncherPlugin.getResourceString(" pref_page_env_variables_label =" +" pref_page_env_variable_invalid_msg =" +" pref_page_dump_config_label =" TomcatLauncherPlugin.getResourceString(" exclude="'*.class'" vminstall =" getVMInstall();" mode =" ILaunchManager.RUN_MODE;" vmrunner =" vmInstall.getVMRunner(mode);" config =" createConfig(label," config =" createConfig(label," sourcelocator =" getSourceLocator(config," trace =" new"> Label : " + label);

trace.append("\n-> ClassToLaunch : " + classToLaunch);

@@ -138,7 +140,7 @@

try {

- ILaunchConfigurationWorkingCopy config = createConfig(label, classToLaunch, classpath, bootClasspath, vmArgs, prgArgs, debug, showInDebugger, false);

+ ILaunchConfigurationWorkingCopy config = createConfig(label, classToLaunch, classpath, bootClasspath, vmArgs, prgArgs, debug, showInDebugger, false, envVariables);

getSourceLocator(config, true);

} catch (CoreException e) {

TomcatLauncherPlugin.log("getSourceLocator failed");

@@ -148,7 +150,7 @@

- static public ILaunchConfigurationWorkingCopy createConfig(String label, String classToLaunch, String[] classpath, String[] bootClasspath, String vmArgs, String prgArgs, boolean debug, boolean showInDebugger, boolean saveConfig) throws CoreException {

+ static public ILaunchConfigurationWorkingCopy createConfig(String label, String classToLaunch, String[] classpath, String[] bootClasspath, String vmArgs, String prgArgs, boolean debug, boolean showInDebugger, boolean saveConfig, Map envVariables) throws CoreException {

IVMInstall vmInstall = getVMInstall();

ILaunchConfigurationType launchType = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType("org.eclipse.jdt.launching.localJavaApplication");

@@ -218,6 +220,8 @@

config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, catalinaBase);

+ config.setAttribute(LaunchManager.ATTR_ENVIRONMENT_VARIABLES, envVariables);


if(saveConfig) {

getSourceLocator(config, false);


diff -ubr --exclude='*.class' ./src/resources_de.properties /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/resources_de.properties
--- ./src/resources_de.properties 2004-06-17 19:06:12.000000000 +0200
+++ /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/resources_de.properties 2008-03-06 21:20:48.000000000 +0100
@@ -32,6 +32,8 @@
pref.page.unselectAll.label=Alles abw\u00E4hlen

pref.page.jvmSettings.label=Setzen Sie die folgenden Felder nur im Falle der bestimmten Anforderungen

pref.page.parameters.label=Zu JVM Parametern hinzuf\u00FCgen


+pref.page.env.variable.invalid.msg=Eine Umgebungsvariable muss in der Form NAME=WERT angegeben werden.

pref.page.bootclasspath.label=Boot Classpath

pref.page.classpath.label=Classpath (Vor dem erstellten Classpath)

pref.page.chooseversion.label=Tomcat Version

diff -ubr --exclude='*.class' ./src/resources.properties /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/resources.properties
--- ./src/resources.properties 2006-10-23 22:01:18.000000000 +0200
+++ /home/jwi/workspace/com.sysdeo.eclipse.tomcat/src/resources.properties 2008-03-06 21:19:57.000000000 +0100
@@ -35,6 +35,8 @@
pref.page.unselectAll.label = Unselect All

pref.page.jvmSettings.label = Set following fields only if you have particular requirements

pref.page.parameters.label = Append to JVM Parameters

+pref.page.env.variables.label = Environment variables

+pref.page.env.variable.invalid.msg = An environment variable must be specified in the form NAME=VALUE.

pref.page.jre.label = JRE

pref.page.bootclasspath.label = Boot Classpath (rt.jar required)

pref.page.classpath.label = Classpath (Before generated classpath)