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
assumeTrue(Boolean.getBoolean("run.slow.tests"));
...
}
}


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

JaxMas?

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);

createControl(parent);

+ 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 @@
TomcatLauncherPlugin.log(trace.toString());



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);

config.doSave();

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.variables.label=Umgebungsvariablen

+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)

Tuesday, November 13, 2007

Linux Installation - Simpler than ever

I had my share of Linux installation in the past, starting with floppy disk based 15 years ago, and used various installation methods, including CD, DVD, bootp based network, local file system, and so on. I am used to things breaking, but of course, I enjoy if everything works well.

This week, I stumbled over a package called UNetbootin. Think of it as a Windows setup.exe for installing Linux. It is actually that simple. You may choose between Fedora 8 (as of this writing, less than one week old), OpenSUSE 10.3, Ubuntu 7.10, Debian Etch, and even a simple partition manager. If you do not have Windows installed, you can choose a different host system: There also are .rpm, and .deb files, as well as a simple shell script.

I am definitely impressed. Most possibly, I will use it for my future installations as well. It's worth spreading the news.

Sunday, November 4, 2007

Crunchy at the outside and a sweet inlet


This is Anjas and Maries preferred dessert: It's a french treat called Crème Brûlée. It's crunchy at the upper side (due to the burnt sugar). (In the case of Averell Dalton, the crunchy thing would most possibly be the bottom.) Don't ask for the inlet. (Cream, eggs yolk, more sugar)

I definitely prefer a double espresso, but I like the fun to use the burner. :-) If you are in company, it's quite likely that the men will sooner or later be scaffling over its ownership.

Why I am writing this? It reminds me of one of my favourite Gary Larson strips: An igloo and two ice bears, one of them saying "Oh, I like these things. Crunchy at the outside and a sweet inlet."