diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..35b0d85f594480a20dd3d6010e5c9088b64ddfa2
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.etlserver.validation;
+
+import org.apache.commons.lang.StringUtils;
+
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+abstract class AbstractValidator implements IValidator
+{
+    private final boolean allowEmptyValues;
+    
+    AbstractValidator(boolean allowEmptyValues)
+    {
+        this.allowEmptyValues = allowEmptyValues;
+    }
+    
+    public final void assertValid(String value)
+    {
+        if (allowEmptyValues)
+        {
+            if (StringUtils.isBlank(value))
+            {
+                return;
+            }
+        } else if (StringUtils.isBlank(value))
+        {
+            throw new UserFailureException("Empty value is not allowed.");
+        }
+        assertValidNonEmptyValue(value);
+    }
+    
+    protected abstract void assertValidNonEmptyValue(String value);
+
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..3195ff3c9f75dc41509c271cb2533e48be1e2d1e
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/AbstractValidatorFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.etlserver.validation;
+
+import java.util.Properties;
+
+import ch.systemsx.cisd.common.utilities.PropertyUtils;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+abstract class AbstractValidatorFactory implements IValidatorFactory
+{
+    static final String ALLOW_EMPTY_VALUES_KEY = "allow-empty-values";
+    
+    protected final boolean allowEmptyValues;
+    
+    AbstractValidatorFactory(Properties properties)
+    {
+        allowEmptyValues = PropertyUtils.getBoolean(properties, ALLOW_EMPTY_VALUES_KEY, false);
+    }
+
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java
index a38ddad28ba4bbe5569a766bc85019422e318319..ba470d65bac8c5e4e71e2b5480a16cd88a92edb4 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/ColumnDefinition.java
@@ -58,8 +58,8 @@ class ColumnDefinition
         IColumnHeaderValidator headerValidator;
         if (headerValidatorName == null)
         {
-            headerValidator =
-                    new RegExBasedValidator(properties.getProperty(HEADER_PATTERN_KEY, ".*"));
+            String headerPattern = properties.getProperty(HEADER_PATTERN_KEY, ".*");
+            headerValidator = new RegExBasedValidator(false, headerPattern);
         } else
         {
             headerValidator =
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java
index 281505896dee9529c785faddfaf3251d43d596af..0a95611fed9c6be15763b561caed545e4e7ff59b 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactory.java
@@ -22,11 +22,11 @@ import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 
 /**
- * 
+ * Factory for validators of numeric values.
  *
  * @author Franz-Josef Elmer
  */
-class NumericValidatorFactory implements IValidatorFactory
+class NumericValidatorFactory extends AbstractValidatorFactory
 {
     static final String VALUE_RANGE_KEY = "value-range";
 
@@ -91,6 +91,11 @@ class NumericValidatorFactory implements IValidatorFactory
                 throw new ConfigurationFailureException("Invalid maximum in range definition: "
                         + rangeDescription);
             }
+            if (maximum < minimum)
+            {
+                throw new ConfigurationFailureException(
+                        "Minimum is larger than maximum in range description: " + rangeDescription);
+            }
         }
 
         void assertInRange(double number)
@@ -108,16 +113,18 @@ class NumericValidatorFactory implements IValidatorFactory
         }
     }
     
-    private final static class NumericValidator implements IValidator
+    private final static class NumericValidator extends AbstractValidator
     {
         private final Range rangeOrNull;
         
-        NumericValidator(Range rangeOrNull)
+        NumericValidator(boolean allowEmptyValues, Range rangeOrNull)
         {
+            super(allowEmptyValues);
             this.rangeOrNull = rangeOrNull;
         }
         
-        public void assertValid(String value)
+        @Override
+        protected void assertValidNonEmptyValue(String value)
         {
             double number = Double.parseDouble(value);
             if (rangeOrNull != null)
@@ -132,8 +139,10 @@ class NumericValidatorFactory implements IValidatorFactory
     
     NumericValidatorFactory(Properties properties)
     {
+        super(properties);
         String valueRange = properties.getProperty(VALUE_RANGE_KEY);
-        validator = new NumericValidator(valueRange == null ? null : new Range(valueRange));
+        Range rangeOrNull = valueRange == null ? null : new Range(valueRange);
+        validator = new NumericValidator(allowEmptyValues, rangeOrNull);
     }
 
     public IValidator createValidator()
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java
index 0b05ceeaeb0376f65d7329f95555150078fe1030..d17c224d737321576fb644586d954bb16b12d47a 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/RegExBasedValidator.java
@@ -21,25 +21,27 @@ import java.util.regex.Pattern;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 
 /**
- * 
+ * Validator based on a regular expression.
  *
  * @author Franz-Josef Elmer
  */
-class RegExBasedValidator implements IValidator, IColumnHeaderValidator
+class RegExBasedValidator extends AbstractValidator implements IColumnHeaderValidator
 {
     private final Pattern pattern;
 
-    RegExBasedValidator(String regularExpression)
+    RegExBasedValidator(boolean allowEmptyValues, String regularExpression)
     {
+        super(allowEmptyValues);
         pattern = Pattern.compile(regularExpression);
     }
 
-    public void assertValid(String value)
+    @Override
+    protected void assertValidNonEmptyValue(String value)
     {
         if (isValidHeader(value) == false)
         {
             throw new UserFailureException("'" + value
-                    + "' dosn't match the following regular expression: " + pattern);
+                    + "' doesn't match the following regular expression: " + pattern);
         }
     }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java
index 5c64afb0bb1b0a00f147a11cff578b2cddd4e8d9..3241e22e92c5cc3fde3ca9a89435805da957e6f1 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactory.java
@@ -25,7 +25,7 @@ import ch.systemsx.cisd.common.utilities.PropertyUtils;
  *
  * @author Franz-Josef Elmer
  */
-class StringValidatorFactory implements IValidatorFactory
+class StringValidatorFactory extends AbstractValidatorFactory
 {
     static final String VALUE_PATTERN_KEY = "value-pattern";
     
@@ -33,8 +33,9 @@ class StringValidatorFactory implements IValidatorFactory
 
     StringValidatorFactory(Properties properties)
     {
+        super(properties);
         String regex = PropertyUtils.getMandatoryProperty(properties, VALUE_PATTERN_KEY);
-        validator = new RegExBasedValidator(regex);
+        validator = new RegExBasedValidator(allowEmptyValues, regex);
     }
     
     public IValidator createValidator()
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java
index 8022f81269366fcd2815a730b7cef5a29d6ba993..2685e2c86b644e2d23f6a5f26538ec64ee8f741f 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactory.java
@@ -39,7 +39,7 @@ class UniqueValidatorFactory implements IValidatorFactory
             validator = AnyValidatorFactory.INSTANCE.createValidator();
         } else
         {
-            validator = new RegExBasedValidator(regex);
+            validator = new RegExBasedValidator(false, regex);
         }
     }
 
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java
index 19c7f130fe5707b8f1e9d085ba202d08f6dd2e06..42f9c19d4b637b8247626a54482ef199a263bfa5 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/NumericValidatorFactoryTest.java
@@ -47,7 +47,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit
     }
     
     @Test
-    public void testMissingOpeningBracket()
+    public void testMissingOpeningBracketInRangeDescription()
     {
         try
         {
@@ -60,7 +60,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit
     }
     
     @Test
-    public void testMissingClosingBracket()
+    public void testMissingClosingBracketInRangeDescription()
     {
         try
         {
@@ -73,7 +73,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit
     }
     
     @Test
-    public void testMissingComma()
+    public void testMissingCommaInRangeDescription()
     {
         try
         {
@@ -85,6 +85,45 @@ public class NumericValidatorFactoryTest extends AssertJUnit
         }
     }
     
+    @Test
+    public void testInvalidMinimumInRangeDescription()
+    {
+        try
+        {
+            createValidator("(abc,3)");
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Invalid minimum in range definition: (abc,3)", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testInvalidMaximumInRangeDescription()
+    {
+        try
+        {
+            createValidator("(1,abc)");
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Invalid maximum in range definition: (1,abc)", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testMinLargerThanMaxInRangeDescription()
+    {
+        try
+        {
+            createValidator("(1,0.999)");
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Minimum is larger than maximum in range description: (1,0.999)", ex.getMessage());
+        }
+    }
+    
     @Test
     public void testInvalidNumber()
     {
@@ -92,8 +131,35 @@ public class NumericValidatorFactoryTest extends AssertJUnit
         IValidator validator = validatorFactory.createValidator();
         
         assertNotANumber(validator, "abc");
-        assertNotANumber(validator, "-0-");
-        assertNotANumber(validator, "");
+        assertNotANumber(validator, " -0-");
+    }
+    
+    @Test
+    public void testEmptyValue()
+    {
+        NumericValidatorFactory validatorFactory = new NumericValidatorFactory(new Properties());
+        IValidator validator = validatorFactory.createValidator();
+
+        assertFailingOnBlankValue(validator, null);
+        assertFailingOnBlankValue(validator, "");
+        assertFailingOnBlankValue(validator, " ");
+    }
+
+    @Test
+    public void testAllowEmptyValues()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(NumericValidatorFactory.ALLOW_EMPTY_VALUES_KEY, "true");
+        properties.setProperty(NumericValidatorFactory.VALUE_RANGE_KEY, "(0,1]");
+        NumericValidatorFactory validatorFactory = new NumericValidatorFactory(properties);
+        IValidator validator = validatorFactory.createValidator();
+
+        validator.assertValid(null);
+        validator.assertValid("");
+        validator.assertValid("  ");
+        validator.assertValid("  0.9999");
+        assertFailingToLarge("> 1.0", validator, "1.25");
+        assertFailingToSmall("<= 0.0", validator, "0.0");
     }
     
     @Test
@@ -144,6 +210,18 @@ public class NumericValidatorFactoryTest extends AssertJUnit
         return validatorFactory.createValidator();
     }
     
+    private void assertFailingOnBlankValue(IValidator validator, String value)
+    {
+        try
+        {
+            validator.assertValid(value);
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("Empty value is not allowed.", ex.getMessage());
+        }
+    }
+
     private void assertNotANumber(IValidator validator, String string)
     {
         try
@@ -152,7 +230,7 @@ public class NumericValidatorFactoryTest extends AssertJUnit
             fail("NumberFormatException expected");
         } catch (NumberFormatException ex)
         {
-            AssertionUtil.assertContains(string, ex.getMessage());
+            AssertionUtil.assertContains(string.trim(), ex.getMessage());
         }
     }
     
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f1c06b796bed84a4f7a2bb057b33f48d4ebfe97
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/StringValidatorFactoryTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.etlserver.validation;
+
+import java.util.Properties;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class StringValidatorFactoryTest extends AssertJUnit
+{
+    @Test
+    public void testMissingPattern()
+    {
+        try
+        {
+            new StringValidatorFactory(new Properties());
+            fail("ConfigurationFailureException expected");
+        } catch (ConfigurationFailureException ex)
+        {
+            assertEquals("Given key '" + StringValidatorFactory.VALUE_PATTERN_KEY
+                    + "' not found in properties '[]'", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testEmptyValuesNotAllowed()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a.*");
+        StringValidatorFactory factory = new StringValidatorFactory(properties);
+        IValidator validator = factory.createValidator();
+
+        validator.assertValid("a");
+        validator.assertValid("a1");
+        validator.assertValid("abc");
+        try
+        {
+            validator.assertValid("bc");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("'bc' doesn't match the following regular expression: a.*", ex.getMessage());
+        }
+        try
+        {
+            validator.assertValid("  ");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("Empty value is not allowed.", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testEmptyValuesAllowed()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a.*");
+        properties.setProperty(StringValidatorFactory.ALLOW_EMPTY_VALUES_KEY, "yes");
+        StringValidatorFactory factory = new StringValidatorFactory(properties);
+        IValidator validator = factory.createValidator();
+        
+        validator.assertValid("a");
+        validator.assertValid("a1");
+        validator.assertValid("abc");
+        validator.assertValid(null);
+        validator.assertValid("");
+        validator.assertValid("  ");
+        try
+        {
+            validator.assertValid("bc");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("'bc' doesn't match the following regular expression: a.*", ex.getMessage());
+        }
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d62579727c95774085fa5ab1fe564e28692789c6
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/validation/UniqueValidatorFactoryTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.etlserver.validation;
+
+import java.util.Properties;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class UniqueValidatorFactoryTest extends AssertJUnit
+{
+    @Test
+    public void testNoPattern()
+    {
+        UniqueValidatorFactory factory = new UniqueValidatorFactory(new Properties());
+        IValidator validator = factory.createValidator();
+        
+        validator.assertValid("a");
+        validator.assertValid("b");
+        try
+        {
+            validator.assertValid("a");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("The following value is not unique: a", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testWithPattern()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(StringValidatorFactory.VALUE_PATTERN_KEY, "a[0-9]*");
+        UniqueValidatorFactory factory = new UniqueValidatorFactory(properties);
+        IValidator validator = factory.createValidator();
+        
+        validator.assertValid("a");
+        validator.assertValid("a1");
+        validator.assertValid("a123");
+        try
+        {
+            validator.assertValid("ab");
+            fail("UserFailureException expected");
+        } catch (UserFailureException ex)
+        {
+            assertEquals("'ab' doesn't match the following regular expression: a[0-9]*", ex.getMessage());
+        }
+    }
+    
+    @Test
+    public void testCreateValidatorReturnsFreshValidator()
+    {
+        UniqueValidatorFactory factory = new UniqueValidatorFactory(new Properties());
+        IValidator validator = factory.createValidator();
+        
+        validator.assertValid("a");
+        
+        validator = factory.createValidator();
+        
+        validator.assertValid("a");
+    }
+}