diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
index f4422d3d2e8da5c1b3dc196359fca9dc3f90c5f6..5b78a235a7023d616e21a53e13081de234f93a14 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/gui/DataSetPropertiesPanel.java
@@ -30,11 +30,14 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.swing.JComboBox;
 import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
@@ -43,6 +46,8 @@ import javax.swing.JTextField;
 import ch.systemsx.cisd.openbis.dss.client.api.gui.DataSetUploadClientModel.NewDataSetInfo;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTOBuilder;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyTypeGroup;
@@ -54,11 +59,32 @@ public class DataSetPropertiesPanel extends JPanel
 {
     private static final long serialVersionUID = 1L;
 
+    /**
+     * An adaptor to convert VocabularyTerms into something that can be put into combo boxes.
+     * 
+     * @author Chandrasekhar Ramakrishnan
+     */
+    private static final class VocabularyTermAdaptor
+    {
+        private final VocabularyTerm term;
+
+        private VocabularyTermAdaptor(VocabularyTerm term)
+        {
+            this.term = term;
+        }
+
+        @Override
+        public String toString()
+        {
+            return term.getLabel();
+        }
+    }
+
     private final DataSetType dataSetType;
 
     private final DataSetUploadClientModel clientModel;
 
-    private final HashMap<String, JTextField> formFields = new HashMap<String, JTextField>();
+    private final HashMap<String, JComponent> formFields = new HashMap<String, JComponent>();
 
     private NewDataSetInfo newDataSetInfo;
 
@@ -123,7 +149,23 @@ public class DataSetPropertiesPanel extends JPanel
             label.setFont(label.getFont().deriveFont(Font.BOLD | Font.ITALIC));
         }
 
+        final JComponent formField;
+        if (propertyType instanceof ControlledVocabularyPropertyType)
+        {
+            formField = createComboBox((ControlledVocabularyPropertyType) propertyType);
+        } else
+        {
+            formField = createTextField(propertyType);
+        }
+        formField.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
+        addFormField(col, row, label, formField);
+        formFields.put(propertyType.getCode(), formField);
+    }
+
+    private JTextField createTextField(final PropertyType propertyType)
+    {
         final JTextField textField = new JTextField();
+        textField.setToolTipText(propertyType.getDescription());
         textField.addActionListener(new ActionListener()
             {
                 public void actionPerformed(ActionEvent e)
@@ -144,9 +186,27 @@ public class DataSetPropertiesPanel extends JPanel
                     // Do nothing
                 }
             });
-        textField.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
-        addFormField(col, row, label, textField);
-        formFields.put(propertyType.getCode(), textField);
+        return textField;
+    }
+
+    private JComboBox createComboBox(final ControlledVocabularyPropertyType propertyType)
+    {
+        final JComboBox comboBox = new JComboBox();
+        comboBox.setToolTipText(propertyType.getDescription());
+        for (VocabularyTerm term : propertyType.getTerms())
+        {
+            comboBox.addItem(new VocabularyTermAdaptor(term));
+        }
+        comboBox.addItemListener(new ItemListener()
+            {
+                public void itemStateChanged(ItemEvent e)
+                {
+                    setPropertyValue(propertyType,
+                            ((VocabularyTermAdaptor) e.getItem()).term.getCode());
+                }
+
+            });
+        return comboBox;
     }
 
     private String getLabelStringForPropertyType(PropertyType propertyType)
@@ -207,8 +267,24 @@ public class DataSetPropertiesPanel extends JPanel
         Map<String, String> props = metadata.getProperties();
         for (String propertyTypeCode : formFields.keySet())
         {
-            JTextField textField = formFields.get(propertyTypeCode);
-            textField.setText(props.get(propertyTypeCode));
+            String propertyValue = props.get(propertyTypeCode);
+            JComponent formField = formFields.get(propertyTypeCode);
+            if (formField instanceof JTextField)
+            {
+                JTextField textField = (JTextField) formField;
+                textField.setText(propertyValue);
+            } else if (formField instanceof JComboBox)
+            {
+                JComboBox comboBox = (JComboBox) formField;
+                for (int i = 0; i < comboBox.getItemCount(); ++i)
+                {
+                    VocabularyTermAdaptor adaptor = (VocabularyTermAdaptor) comboBox.getItemAt(i);
+                    if (adaptor.term.getCode().equals(propertyValue))
+                    {
+                        comboBox.setSelectedIndex(i);
+                    }
+                }
+            }
         }
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
index e4c799f70a501df03a65f5270208ac4d755ac0a1..4ee17e744d4afd40ff95397c0125b5886cb2b1ac 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
@@ -41,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDatabaseInstanceDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
@@ -387,11 +388,31 @@ public class GeneralInformationService extends AbstractServer<IGeneralInformatio
         List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType> privateDataSetTypes =
                 commonServer.listDataSetTypes(sessionToken);
 
+        HashMap<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>> vocabTerms =
+                getVocabularyTermsMap(sessionToken);
+
         ArrayList<DataSetType> dataSetTypes = new ArrayList<DataSetType>();
         for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType privateDataSetType : privateDataSetTypes)
         {
-            dataSetTypes.add(Translator.translate(privateDataSetType));
+            dataSetTypes.add(Translator.translate(privateDataSetType, vocabTerms));
         }
         return dataSetTypes;
     }
+
+    private HashMap<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>> getVocabularyTermsMap(
+            String sessionToken)
+    {
+        HashMap<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>> vocabTerms =
+                new HashMap<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>>();
+        List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary> privateVocabularies =
+                commonServer.listVocabularies(sessionToken, false, false);
+        for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary privateVocabulary : privateVocabularies)
+        {
+            Set<ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm> privateTerms =
+                    commonServer.listVocabularyTerms(sessionToken, privateVocabulary);
+
+            vocabTerms.put(privateVocabulary, Translator.translate(privateTerms));
+        }
+        return vocabTerms;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
index af2af7e135aad9af90afc520eff8c4b72c1e2aee..5b64b638eb98f3720b7b82295035dcf4713990c0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
@@ -16,9 +16,15 @@
 
 package ch.systemsx.cisd.openbis.generic.server.api.v1;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.ControlledVocabularyPropertyType.ControlledVocabularyPropertyTypeInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.DataSetInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSetType;
@@ -32,8 +38,10 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyTypeGroup.Prop
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample.SampleInitializer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleLevel;
 
@@ -97,7 +105,8 @@ public class Translator
     }
 
     static DataSetType translate(
-            ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType privateDataSetType)
+            ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType privateDataSetType,
+            HashMap<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary, List<ControlledVocabularyPropertyType.VocabularyTerm>> vocabTerms)
     {
         DataSetTypeInitializer initializer = new DataSetTypeInitializer();
         initializer.setCode(privateDataSetType.getCode());
@@ -124,15 +133,38 @@ public class Translator
                 groupInitializer = new PropertyTypeGroupInitializer();
                 sectionName = thisSectionName;
             }
-            PropertyTypeInitializer ptInitializer = new PropertyTypeInitializer();
+            PropertyTypeInitializer ptInitializer;
             ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType propertyType =
                     dstpt.getPropertyType();
+
+            boolean isControlledVocabulary =
+                    propertyType.getDataType().getCode() == DataTypeCode.CONTROLLEDVOCABULARY;
+            if (isControlledVocabulary)
+            {
+                ControlledVocabularyPropertyTypeInitializer cvptInitializer =
+                        new ControlledVocabularyPropertyTypeInitializer();
+
+                cvptInitializer.setTerms(vocabTerms.get(propertyType.getVocabulary()));
+                ptInitializer = cvptInitializer;
+            } else
+            {
+                ptInitializer = new PropertyTypeInitializer();
+            }
+
             ptInitializer.setDataType(propertyType.getDataType().getCode());
             ptInitializer.setCode(propertyType.getCode());
             ptInitializer.setLabel(propertyType.getLabel());
             ptInitializer.setDescription(propertyType.getDescription());
             ptInitializer.setMandatory(dstpt.isMandatory());
-            groupInitializer.addPropertyType(new PropertyType(ptInitializer));
+
+            if (isControlledVocabulary)
+            {
+                groupInitializer.addPropertyType(new ControlledVocabularyPropertyType(
+                        (ControlledVocabularyPropertyTypeInitializer) ptInitializer));
+            } else
+            {
+                groupInitializer.addPropertyType(new PropertyType(ptInitializer));
+            }
         }
         // Finally set the group
         initializer.addPropertyTypeGroup(new PropertyTypeGroup(groupInitializer));
@@ -140,8 +172,31 @@ public class Translator
         return new DataSetType(initializer);
     }
 
-    private Translator()
+    public static List<ControlledVocabularyPropertyType.VocabularyTerm> translate(
+            Set<ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm> privateTerms)
     {
+        ArrayList<ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm> sortedTerms =
+                new ArrayList<ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm>(
+                        privateTerms);
+        Collections.sort(sortedTerms,
+                new Comparator<ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm>()
+                    {
+
+                        public int compare(VocabularyTerm o1, VocabularyTerm o2)
+                        {
+                            return o1.getOrdinal().compareTo(o2.getOrdinal());
+                        }
+
+                    });
+        ArrayList<ControlledVocabularyPropertyType.VocabularyTerm> terms =
+                new ArrayList<ControlledVocabularyPropertyType.VocabularyTerm>();
+        for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm privateTerm : sortedTerms)
+        {
+            terms.add(new ControlledVocabularyPropertyType.VocabularyTerm(privateTerm.getCode(),
+                    privateTerm.getCodeOrLabel()));
+        }
+
+        return terms;
     }
 
     public static DataSet translate(ExternalData externalDatum)
@@ -160,4 +215,8 @@ public class Translator
 
         return new DataSet(initializer);
     }
+
+    private Translator()
+    {
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/ControlledVocabularyPropertyType.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/ControlledVocabularyPropertyType.java
new file mode 100644
index 0000000000000000000000000000000000000000..760a96d839ee82fc2fa0f4900d8c521ce789289c
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/ControlledVocabularyPropertyType.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2011 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.openbis.generic.shared.api.v1.dto;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class ControlledVocabularyPropertyType extends PropertyType
+{
+    private static final long serialVersionUID = 1L;
+
+    public static class VocabularyTerm implements Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final String code;
+
+        private final String label;
+
+        public VocabularyTerm(String code, String label)
+        {
+            this.code = code;
+            this.label = label;
+        }
+
+        public String getCode()
+        {
+            return code;
+        }
+
+        public String getLabel()
+        {
+            return label;
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (obj == this)
+            {
+                return true;
+            }
+            if (obj instanceof VocabularyTerm == false)
+            {
+                return false;
+            }
+
+            EqualsBuilder builder = new EqualsBuilder();
+            VocabularyTerm other = (VocabularyTerm) obj;
+            builder.append(getCode(), other.getCode());
+            return builder.isEquals();
+        }
+
+        @Override
+        public int hashCode()
+        {
+            HashCodeBuilder builder = new HashCodeBuilder();
+            builder.append(getCode());
+            return builder.toHashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
+            builder.append(getCode());
+            builder.append(getLabel());
+            return builder.toString();
+        }
+    }
+
+    public static class ControlledVocabularyPropertyTypeInitializer extends PropertyTypeInitializer
+    {
+        private final ArrayList<VocabularyTerm> terms = new ArrayList<VocabularyTerm>();
+
+        public void setTerms(List<VocabularyTerm> validValues)
+        {
+            this.terms.clear();
+            this.terms.addAll(validValues);
+        }
+    }
+
+    private final ArrayList<VocabularyTerm> terms;
+
+    /**
+     * @param initializer
+     */
+    public ControlledVocabularyPropertyType(ControlledVocabularyPropertyTypeInitializer initializer)
+    {
+        super(initializer);
+        terms = initializer.terms;
+        if (terms == null || terms.isEmpty())
+        {
+            throw new IllegalArgumentException(
+                    "A controlled vocabulary property type must have terms");
+        }
+    }
+
+    public List<VocabularyTerm> getTerms()
+    {
+        return terms;
+    }
+
+    @Override
+    protected void appendFieldsToStringBuilder(ToStringBuilder builder)
+    {
+        builder.append(terms);
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/PropertyType.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/PropertyType.java
index dc0520d70a586605a75bb51eba0c2e68b2c39913..d87dfaf1a59cb7201a90767dbf60f5074e7222de 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/PropertyType.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/PropertyType.java
@@ -30,7 +30,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-public final class PropertyType implements Serializable
+public class PropertyType implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
@@ -40,7 +40,7 @@ public final class PropertyType implements Serializable
      * 
      * @author Chandrasekhar Ramakrishnan
      */
-    public static final class PropertyTypeInitializer
+    public static class PropertyTypeInitializer
     {
         private DataTypeCode dataType;
 
@@ -101,7 +101,6 @@ public final class PropertyType implements Serializable
         {
             this.mandatory = mandatory;
         }
-
     }
 
     private final DataTypeCode dataType;
@@ -214,6 +213,15 @@ public final class PropertyType implements Serializable
         builder.append(getLabel());
         builder.append(getDescription());
         builder.append(isMandatory() ? "mandatory" : "optional");
+        this.appendFieldsToStringBuilder(builder);
         return builder.toString();
     }
+
+    /**
+     * For subclasses to override
+     */
+    protected void appendFieldsToStringBuilder(ToStringBuilder builder)
+    {
+
+    }
 }