diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
index 590c678864c7abf95e2bc4572c6b6f3cb74f5a31..16201e1b94fc09efc301bfb3b5612c0a405a7641 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
@@ -191,6 +191,9 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     @JsonProperty
     private List<Attachment> attachments;
 
+    @JsonProperty
+    private Map<String, String> metaData;
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public SampleFetchOptions getFetchOptions()
@@ -1177,6 +1180,17 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         return relationships == null ? new Relationship() : relationships.get(childId);
     }
 
+    @JsonIgnore
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/SampleType.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/SampleType.java
index a52bb94ab3f4e6700efe2998d3f7e1d141ac97eb..c34d87d9c6dc968e6612d66e68e7d8cf2c276ab0 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/SampleType.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/SampleType.java
@@ -18,6 +18,7 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample;
 import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -92,6 +93,10 @@ public class SampleType implements Serializable, ICodeHolder, IDescriptionHolder
     @JsonProperty
     private Plugin validationPlugin;
 
+    @JsonProperty
+    private Map<String, String> metaData;
+
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public SampleTypeFetchOptions getFetchOptions()
@@ -309,6 +314,18 @@ public class SampleType implements Serializable, ICodeHolder, IDescriptionHolder
         this.validationPlugin = validationPlugin;
     }
 
+    @JsonIgnore
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
+
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
index 6f97aa030dbfc4d0a205c85482508ff5eec49156..91d7b64f3efa734433818ba9fbeed0d4c080c411 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleCreation.java
@@ -79,6 +79,8 @@ public class SampleCreation implements ICreation, ICreationIdHolder, IProperties
 
     private CreationId creationId;
 
+    private Map<String, String> metaData;
+
     public IEntityTypeId getTypeId()
     {
         return typeId;
@@ -222,6 +224,16 @@ public class SampleCreation implements ICreation, ICreationIdHolder, IProperties
         this.attachments = attachments;
     }
 
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
     @Override
     public void setProperty(String propertyName, String propertyValue)
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleTypeCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleTypeCreation.java
index c1bbdc54064dd448d526e749828449ca78dc0acf..6599555ea9ce03ea997116c86bb27044b3784a19 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleTypeCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/create/SampleTypeCreation.java
@@ -16,6 +16,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create;
 
 import java.util.List;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.ObjectToString;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.create.IEntityTypeCreation;
@@ -54,6 +55,8 @@ public class SampleTypeCreation implements IEntityTypeCreation
 
     private List<PropertyAssignmentCreation> propertyAssignments;
 
+    private Map<String, String> metaData;
+
     @Override
     public String getCode()
     {
@@ -172,6 +175,16 @@ public class SampleTypeCreation implements IEntityTypeCreation
         this.propertyAssignments = propertyAssignments;
     }
 
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
     @Override
     public String toString()
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleTypeUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleTypeUpdate.java
index 9051aedf87e434ddf99d63b0062f6875adae3520..cff30959d23ec4e610e3aefe81333666fd6a9128 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleTypeUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleTypeUpdate.java
@@ -16,7 +16,9 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.update;
 
 import java.util.List;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateMapValues;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
@@ -70,6 +72,9 @@ public class SampleTypeUpdate implements IEntityTypeUpdate
     @JsonProperty
     private PropertyAssignmentListUpdateValue propertyAssignments = new PropertyAssignmentListUpdateValue();
 
+    @JsonProperty
+    private ListUpdateMapValues metaData = new ListUpdateMapValues();
+
     @Override
     @JsonIgnore
     public IEntityTypeId getTypeId()
@@ -217,6 +222,18 @@ public class SampleTypeUpdate implements IEntityTypeUpdate
         propertyAssignments.setActions(actions);
     }
 
+    @JsonIgnore
+    public ListUpdateMapValues getMetaData()
+    {
+        return metaData;
+    }
+
+    @JsonIgnore
+    public void setMetaDataActions(List<ListUpdateAction<Object>> actions)
+    {
+        metaData.setActions(actions);
+    }
+
     @Override
     public String toString()
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
index cdb005fe94b25b50489acc207eec6916b894546e..063192849d106c023b001a8534ed478694aca3db 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/update/SampleUpdate.java
@@ -19,6 +19,7 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.*;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -27,12 +28,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.update.AttachmentListUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.ObjectToString;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IObjectUpdate;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IUpdate;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IdListUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue.ListUpdateAction;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.RelationshipUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.IProjectId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
@@ -101,6 +97,9 @@ public class SampleUpdate implements IUpdate, IPropertiesHolder, IObjectUpdate<I
     @JsonProperty
     private AttachmentListUpdateValue attachments = new AttachmentListUpdateValue();
 
+    @JsonProperty
+    private ListUpdateMapValues metaData = new ListUpdateMapValues();
+
     @Override
     @JsonIgnore
     public ISampleId getObjectId()
@@ -534,6 +533,18 @@ public class SampleUpdate implements IUpdate, IPropertiesHolder, IObjectUpdate<I
         setProperty(propertyName, propertyValue);
     }
 
+    @JsonIgnore
+    public ListUpdateMapValues getMetaData()
+    {
+        return metaData;
+    }
+
+    @JsonIgnore
+    public void setMetaDataActions(List<ListUpdateAction<Object>> actions)
+    {
+        metaData.setActions(actions);
+    }
+
     @Override
     public String toString()
     {
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/Sample.js b/api-openbis-javascript/src/v3/as/dto/sample/Sample.js
index 99695a3495eb5384531b431f92972339cf6ff01e..23852de463d088b31a5210fd5e07370233c8bc96 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/Sample.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/Sample.js
@@ -48,6 +48,7 @@ define([ "stjs", "util/Exceptions", "as/dto/common/Relationship" ], function(stj
 		prototype.registrator = null;
 		prototype.modifier = null;
 		prototype.attachments = null;
+		prototype.metaData = null;
 		prototype.getFetchOptions = function() {
 			return this.fetchOptions;
 		};
@@ -539,6 +540,12 @@ define([ "stjs", "util/Exceptions", "as/dto/common/Relationship" ], function(stj
 		prototype.setAttachments = function(attachments) {
 			this.attachments = attachments;
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaData = function(metaData) {
+            this.metaData = metaData;
+        };
 		prototype.toString = function() {
 			return "Sample " + this.permId;
 		};
@@ -642,7 +649,11 @@ define([ "stjs", "util/Exceptions", "as/dto/common/Relationship" ], function(stj
 		attachments : {
 			name : "List",
 			arguments : [ "Attachment" ]
-		}
+		},
+		metaData: {
+            name: "Map",
+            arguments: ["String", "String"]
+        }
 	});
 	return Sample;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/SampleType.js b/api-openbis-javascript/src/v3/as/dto/sample/SampleType.js
index 940a90de026549ff3a39d8a2d8953efe347a226d..758e5708d18d41581ddf8e6ecd38b16acfdb9b44 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/SampleType.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/SampleType.js
@@ -26,6 +26,7 @@ define(['stjs'], function (stjs) {
       prototype.propertyAssignments = null
       prototype.semanticAnnotations = null
       prototype.validationPlugin = null
+      prototype.metaData = null;
       prototype.getPropertyAssignments = function () {
         if (
           this.getFetchOptions() &&
@@ -143,6 +144,12 @@ define(['stjs'], function (stjs) {
       prototype.setModificationDate = function (modificationDate) {
         this.modificationDate = modificationDate
       }
+      prototype.getMetaData = function() {
+        return this.metaData;
+      };
+      prototype.setMetaData = function(metaData) {
+        this.metaData = metaData;
+      };
       prototype.toString = function () {
         return this.getCode()
       }
@@ -159,7 +166,11 @@ define(['stjs'], function (stjs) {
         name: 'List',
         arguments: ['SemanticAnnotation']
       },
-      validationPlugin: 'Plugin'
+      validationPlugin: 'Plugin',
+      metaData: {
+        name: "Map",
+        arguments: ["String", "String"]
+      }
     }
   )
   return SampleType
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/create/SampleCreation.js b/api-openbis-javascript/src/v3/as/dto/sample/create/SampleCreation.js
index 61e717b3f8ecc226262cbc8ccbe9befcbb11f9cb..74dda788162471f102d1c199070352967580940c 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/create/SampleCreation.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/create/SampleCreation.js
@@ -24,6 +24,7 @@ define([ "stjs", "as/dto/common/Relationship" ], function(stjs, Relationship) {
 		prototype.attachments = null;
 		prototype.creationId = null;
 		prototype.autoGeneratedCode = null;
+		prototype.metaData = null;
 		prototype.getTypeId = function() {
 			return this.typeId;
 		};
@@ -218,6 +219,12 @@ define([ "stjs", "as/dto/common/Relationship" ], function(stjs, Relationship) {
 		prototype.setCreationId = function(creationId) {
 			this.creationId = creationId;
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaData = function(metaData) {
+            this.metaData = metaData;
+        };
 	}, {
 		typeId : "IEntityTypeId",
 		experimentId : "IExperimentId",
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/create/SampleTypeCreation.js b/api-openbis-javascript/src/v3/as/dto/sample/create/SampleTypeCreation.js
index c9017945b17f6616b93c03fd28ceeb65a2a21e60..f33472654daafc23d554e752c9706a9d57b91467 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/create/SampleTypeCreation.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/create/SampleTypeCreation.js
@@ -18,6 +18,7 @@ define([ "stjs" ], function(stjs) {
 		prototype.showParentMetadata = false;
 		prototype.validationPluginId = null;
 		prototype.propertyAssignments = null;
+		prototype.metaData = null;
 
 		prototype.getCode = function() {
 			return this.code;
@@ -85,13 +86,23 @@ define([ "stjs" ], function(stjs) {
 		prototype.setPropertyAssignments = function(propertyAssignments) {
 			this.propertyAssignments = propertyAssignments;
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaData = function(metaData) {
+            this.metaData = metaData;
+        };
 
 	}, {
 		validationPluginId : "IPluginId",
 		propertyAssignments : {
 			name : "List",
 			arguments : [ "PropertyAssignmentCreation" ]
-		}
+		},
+		metaData: {
+            name: "Map",
+            arguments: ["String", "String"]
+        }
 	});
 	return SampleTypeCreation;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/update/SampleTypeUpdate.js b/api-openbis-javascript/src/v3/as/dto/sample/update/SampleTypeUpdate.js
index dac4b2366d411b2559309c2f2c2b6e23522b9c94..a0d0603e3f8e88c9cacd711bee7d309b3152be07 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/update/SampleTypeUpdate.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/update/SampleTypeUpdate.js
@@ -1,5 +1,7 @@
-define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/update/PropertyAssignmentListUpdateValue" ], function(stjs, FieldUpdateValue, 
-		PropertyAssignmentListUpdateValue) {
+define([ "stjs", "as/dto/common/update/FieldUpdateValue",
+            "as/dto/entitytype/update/PropertyAssignmentListUpdateValue",
+            "as/dto/common/update/ListUpdateMapValues" ], function(stjs, FieldUpdateValue,
+            PropertyAssignmentListUpdateValue, ListUpdateMapValues) {
 	var SampleTypeUpdate = function() {
 		this.description = new FieldUpdateValue();
 		this.generatedCodePrefix = new FieldUpdateValue();
@@ -11,6 +13,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 		this.showParentMetadata = new FieldUpdateValue();
 		this.validationPluginId = new FieldUpdateValue();
 		this.propertyAssignments = new PropertyAssignmentListUpdateValue();
+		this.metaData = new ListUpdateMapValues();
 	};
 	stjs.extend(SampleTypeUpdate, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.sample.update.SampleTypeUpdate';
@@ -26,6 +29,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 		prototype.showParentMetadata = null;
 		prototype.validationPluginId = null;
 		prototype.propertyAssignments = null;
+		prototype.metaData = null;
 
 		prototype.getObjectId = function() {
 			return this.getTypeId();
@@ -96,6 +100,12 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 		prototype.setPropertyAssignmentActions = function(actions) {
 			this.propertyAssignments.setActions(actions);
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaDataActions = function(actions) {
+            this.metaData.setActions(actions);
+        };
 	}, {
 		typeId : "IEntityTypeId",
 		description : {
@@ -134,7 +144,8 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 			name : "FieldUpdateValue",
 			arguments : [ "IPluginId" ]
 		},
-		propertyAssignments : "PropertyAssignmentListUpdateValue"
+		propertyAssignments : "PropertyAssignmentListUpdateValue",
+        metaData : "ListUpdateMapValues"
 	});
 	return SampleTypeUpdate;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/sample/update/SampleUpdate.js b/api-openbis-javascript/src/v3/as/dto/sample/update/SampleUpdate.js
index 216bd09c018845360a10c7b68d84e27cf41d5b89..b542d7d1f89354d1d2d71871895aff27c200d188 100644
--- a/api-openbis-javascript/src/v3/as/dto/sample/update/SampleUpdate.js
+++ b/api-openbis-javascript/src/v3/as/dto/sample/update/SampleUpdate.js
@@ -2,9 +2,10 @@
  * @author pkupczyk
  */
 define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/IdListUpdateValue", 
-			"as/dto/attachment/update/AttachmentListUpdateValue", "as/dto/common/update/RelationshipUpdate" ], 
-			function(stjs, FieldUpdateValue,
-					IdListUpdateValue, AttachmentListUpdateValue, RelationshipUpdate) {
+			"as/dto/attachment/update/AttachmentListUpdateValue", "as/dto/common/update/RelationshipUpdate",
+			"as/dto/common/update/ListUpdateMapValues" ],
+			function(stjs, FieldUpdateValue, IdListUpdateValue, AttachmentListUpdateValue,
+			RelationshipUpdate, ListUpdateMapValues) {
 	var SampleUpdate = function() {
 		this.properties = {};
 		this.experimentId = new FieldUpdateValue();
@@ -17,6 +18,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		this.childIds = new IdListUpdateValue();
 		this.relationships = {};
 		this.attachments = new AttachmentListUpdateValue();
+		this.metaData = new ListUpdateMapValues();
 	};
 	stjs.extend(SampleUpdate, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.sample.update.SampleUpdate';
@@ -38,6 +40,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		prototype.childIds = null;
 		prototype.relationships = null;
 		prototype.attachments = null;
+		prototype.metaData = null;
 
 		prototype.getObjectId = function() {
 			return this.getSampleId();
@@ -252,6 +255,12 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		prototype.setAttachmentsActions = function(actions) {
 			this.attachments.setActions(actions);
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaDataActions = function(actions) {
+            this.metaData.setActions(actions);
+        };
 	}, {
 		sampleId : "ISampleId",
 		experimentId : {
@@ -294,7 +303,8 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 			name : "Map",
 			arguments : [ "ISampleId", "RelationshipUpdate" ]
 		},
-		attachments : "AttachmentListUpdateValue"
+		attachments : "AttachmentListUpdateValue",
+		metaData : "ListUpdateMapValues"
 	});
 	return SampleUpdate;
 })
\ No newline at end of file
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
index 00c15b29cf379adf7b2b5f2ab45e9a4d56c5b413..7c21b1e6d537526d7416bfc48166bc972715ca27 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleExecutor.java
@@ -151,6 +151,7 @@ public class CreateSampleExecutor extends AbstractCreateEntityExecutor<SampleCre
                     sample.setPermId(createdPermId);
                     sample.setRegistrator(person);
                     RelationshipUtils.updateModificationDateAndModifier(sample, person, timeStamp);
+                    sample.setMetaData(creation.getMetaData());
                     samples.add(sample);
                 }
 
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleTypesExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleTypesExecutor.java
index 3bf87c8b40be9821714ae2f307751857f6290641..ee955b767c13e0a10bf1b4a5ba47d5f36eec88bf 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleTypesExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/CreateSampleTypesExecutor.java
@@ -75,6 +75,7 @@ public class CreateSampleTypesExecutor extends AbstractCreateEntityTypeExecutor<
         type.setShowContainer(creation.isShowContainer());
         type.setShowParents(creation.isShowParents());
         type.setShowParentMetadata(creation.isShowParentMetadata());
+        type.setMetaData(creation.getMetaData());
     }
 
     @Override
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
index 089b1d4f529f27970ce7726c212cba0ec955782a..f8483e7da3d2ec9460aeac1fbb3b2c301e7c64d9 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleExecutor.java
@@ -15,13 +15,11 @@
  */
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.stereotype.Component;
@@ -185,6 +183,7 @@ public class UpdateSampleExecutor extends AbstractUpdateEntityExecutor<SampleUpd
         updateSamplePropertyExecutor.update(context, batch);
         updateTags(context, batch);
         updateAttachments(context, batch);
+        updateMetaData(context, batch);
 
         for (SamplePE entity : experimentOrProjectSamples)
         {
@@ -203,6 +202,68 @@ public class UpdateSampleExecutor extends AbstractUpdateEntityExecutor<SampleUpd
         }
     }
 
+    private void updateMetaData(final IOperationContext context, final MapBatch<SampleUpdate, SamplePE> batch) {
+        new MapBatchProcessor<SampleUpdate, SamplePE>(context, batch)
+        {
+            @Override
+            public void process(SampleUpdate update, SamplePE entity)
+            {
+                Map<String, String> metaData = new HashMap<>();
+                if(entity.getMetaData() != null) {
+                    metaData.putAll(entity.getMetaData());
+                }
+                ListUpdateValue.ListUpdateActionSet<?> lastSetAction = null;
+                AtomicBoolean metaDataChanged = new AtomicBoolean(false);
+                for (ListUpdateValue.ListUpdateAction<Object> action : update.getMetaData().getActions())
+                {
+                    if (action instanceof ListUpdateValue.ListUpdateActionAdd<?>)
+                    {
+                        addTo(metaData, action, metaDataChanged);
+                    } else if (action instanceof ListUpdateValue.ListUpdateActionRemove<?>)
+                    {
+                        for (String key : (Collection<String>) action.getItems())
+                        {
+                            metaDataChanged.set(true);
+                            metaData.remove(key);
+                        }
+                    } else if (action instanceof ListUpdateValue.ListUpdateActionSet<?>)
+                    {
+                        lastSetAction = (ListUpdateValue.ListUpdateActionSet<?>) action;
+                    }
+                }
+                if (lastSetAction != null)
+                {
+                    metaData.clear();
+                    addTo(metaData, lastSetAction, metaDataChanged);
+                }
+                if (metaDataChanged.get())
+                {
+                    entity.setMetaData(metaData.isEmpty() ? null : metaData);
+                }
+            }
+
+            @Override
+            public IProgress createProgress(SampleUpdate update, SamplePE entity, int objectIndex, int totalObjectCount)
+            {
+                return new UpdateRelationProgress(update, entity, "sample-tag", objectIndex, totalObjectCount);
+            }
+
+            @SuppressWarnings("unchecked")
+            private void addTo(Map<String, String> metaData, ListUpdateValue.ListUpdateAction<?> lastSetAction, AtomicBoolean metaDataChanged)
+            {
+                Collection<Map<String, String>> maps = (Collection<Map<String, String>>) lastSetAction.getItems();
+                for (Map<String, String> map : maps)
+                {
+                    if (map.isEmpty() == false)
+                    {
+                        metaDataChanged.set(true);
+                        metaData.putAll(map);
+                    }
+                }
+            }
+        };
+    }
+
     private void updateTags(final IOperationContext context, final MapBatch<SampleUpdate, SamplePE> batch)
     {
         new MapBatchProcessor<SampleUpdate, SamplePE>(context, batch)
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleTypeExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleTypeExecutor.java
index d587dcee175d9a1c304d904193f5441f4cb9b3af..3a9e1ca6afe7ba44819517ec380658bd4c7a614f 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleTypeExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/sample/UpdateSampleTypeExecutor.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.sample;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateValue;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -26,6 +27,11 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntity
 import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /**
  * 
  *
@@ -75,6 +81,7 @@ public class UpdateSampleTypeExecutor
         {
             type.setGeneratedFromHierarchyDepth(Boolean.TRUE.equals(update.isShowParents().getValue()) ? 1 : 0);
         }
+        updateMetaData(type, update);
     }
 
     @Override
@@ -83,4 +90,53 @@ public class UpdateSampleTypeExecutor
         return updateSampleTypePropertyTypesExecutor;
     }
 
+
+    private void updateMetaData(SampleTypePE type, SampleTypeUpdate update) {
+        Map<String, String> metaData = new HashMap<>();
+        if(type.getMetaData() != null) {
+            metaData.putAll(type.getMetaData());
+        }
+        ListUpdateValue.ListUpdateActionSet<?> lastSetAction = null;
+        AtomicBoolean metaDataChanged = new AtomicBoolean(false);
+        for (ListUpdateValue.ListUpdateAction<Object> action : update.getMetaData().getActions())
+        {
+            if (action instanceof ListUpdateValue.ListUpdateActionAdd<?>)
+            {
+                addTo(metaData, action, metaDataChanged);
+            } else if (action instanceof ListUpdateValue.ListUpdateActionRemove<?>)
+            {
+                for (String key : (Collection<String>) action.getItems())
+                {
+                    metaDataChanged.set(true);
+                    metaData.remove(key);
+                }
+            } else if (action instanceof ListUpdateValue.ListUpdateActionSet<?>)
+            {
+                lastSetAction = (ListUpdateValue.ListUpdateActionSet<?>) action;
+            }
+        }
+        if (lastSetAction != null)
+        {
+            metaData.clear();
+            addTo(metaData, lastSetAction, metaDataChanged);
+        }
+        if (metaDataChanged.get())
+        {
+            type.setMetaData(metaData.isEmpty() ? null : metaData);
+        }
+    }
+
+    private void addTo(Map<String, String> metaData, ListUpdateValue.ListUpdateAction<?> lastSetAction, AtomicBoolean metaDataChanged)
+    {
+        Collection<Map<String, String>> maps = (Collection<Map<String, String>>) lastSetAction.getItems();
+        for (Map<String, String> map : maps)
+        {
+            if (!map.isEmpty())
+            {
+                metaDataChanged.set(true);
+                metaData.putAll(map);
+            }
+        }
+    }
+
 }
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleBaseRecord.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleBaseRecord.java
index aa6087e9fccdc974a343e83916e11a68bb5adf04..996077f78d9a319eff3fe29ca520893f2a435613 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleBaseRecord.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleBaseRecord.java
@@ -49,4 +49,6 @@ public class SampleBaseRecord extends ObjectBaseRecord
 
     public Date modificationDate;
 
+    public String metaData;
+
 }
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java
index 0b766b36f3e5861ff9a6a080a13473c7057ab3e0..abc5890fe56c8a328c19fc5e0ef55e2f1aa426a7 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java
@@ -26,6 +26,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.property.PropertyR
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.property.SamplePropertyRecord;
 import ch.systemsx.cisd.common.db.mapper.LongArrayMapper;
 import ch.systemsx.cisd.common.db.mapper.LongSetMapper;
+import ch.systemsx.cisd.common.db.mapper.StringMapMapper;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.common.HistoryPropertyRecordDataObjectBinding;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.common.PropertyRecordDataObjectBinding;
 import it.unimi.dsi.fastutil.longs.LongSet;
@@ -41,7 +42,7 @@ public interface SampleQuery extends ObjectQuery
             + "sc.code as containerCode, s.registration_timestamp as registrationDate, "
             + "s.modification_timestamp as modificationDate, s.frozen as frozen, s.frozen_for_comp as frozenForComponents, "
             + "s.frozen_for_children as frozenForChildren, s.frozen_for_parents as frozenForParents, "
-            + "s.frozen_for_data as frozenForDataSets "
+            + "s.frozen_for_data as frozenForDataSets, s.meta_data as metaData "
             + "from samples s left join spaces sp on s.space_id = sp.id "
             + "left join projects p on s.proj_id = p.id "
             + "left join samples sc on s.samp_id_part_of = sc.id "
@@ -55,7 +56,7 @@ public interface SampleQuery extends ObjectQuery
     @Select(sql =
             "select st.id, st.code, st.description, st.is_listable as listable, st.is_subcode_unique as subcodeUnique, st.is_auto_generated_code as autoGeneratedCode, "
                     + "st.show_parent_metadata as showParentMetadata, st.generated_code_prefix as generatedCodePrefix, st.generated_from_depth as generatedFromDepth, st.part_of_depth as partOfDepth, "
-                    + "st.modification_timestamp as modificationDate from sample_types st where st.id = any(?{1})", parameterBindings = {
+                    + "st.modification_timestamp as modificationDate, st.meta_data as metaData from sample_types st where st.id = any(?{1})", parameterBindings = {
             LongSetMapper.class }, fetchSize = FETCH_SIZE)
     public List<SampleTypeBaseRecord> getTypes(LongSet sampleTypeIds);
 
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTranslator.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTranslator.java
index 85237008b97491195963f337ea40902e5227d1ed..b97c31bab3742353ce0c4697344153ed6c439a77 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTranslator.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTranslator.java
@@ -321,6 +321,7 @@ public class SampleTranslator extends AbstractCachingTranslator<Long, Sample, Sa
         result.setFrozenForDataSets(baseRecord.frozenForDataSets);
         result.setModificationDate(baseRecord.modificationDate);
         result.setRegistrationDate(baseRecord.registrationDate);
+        result.setMetaData(CommonUtils.asMap(baseRecord.metaData));
 
         if (fetchOptions.hasType())
         {
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeBaseRecord.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeBaseRecord.java
index 57727bfd407cf572816cddc9f19d58d6062a652c..49af9577a3e90ef78be3d8e4c54e5095f54a99f6 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeBaseRecord.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeBaseRecord.java
@@ -45,4 +45,6 @@ public class SampleTypeBaseRecord extends ObjectBaseRecord
 
     public Date modificationDate;
 
+    public String metaData;
+
 }
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
index 2d8d2c08af073b346c2bda8503d33f144a5de1f2..f972c5b5700e6c5fc843830d8c10b2ce3cf76710 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
@@ -19,6 +19,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.CommonUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -106,6 +107,7 @@ public class SampleTypeTranslator extends AbstractCachingTranslator<Long, Sample
         result.setShowParentMetadata(baseRecord.showParentMetadata);
         result.setSubcodeUnique(baseRecord.subcodeUnique);
         result.setModificationDate(baseRecord.modificationDate);
+        result.setMetaData(CommonUtils.asMap(baseRecord.metaData));
 
         if (fetchOptions.hasPropertyAssignments())
         {
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DatabaseVersionHolder.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DatabaseVersionHolder.java
index 37bc0a60aeb3c0dc510fdb633709866c44340288..613f8b5b0ff14e93354aad9642cb9dd1746ea92e 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DatabaseVersionHolder.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/DatabaseVersionHolder.java
@@ -25,7 +25,7 @@ public final class DatabaseVersionHolder
     /**
      * Current version of the database.
      */
-    private static final String DATABASE_VERSION = "191";
+    private static final String DATABASE_VERSION = "192";
 
     /** Current version of the database INDICES. */
     private static final String DATABASE_FULL_TEXT_SEARCH_DOCUMENT_VERSION = "002";
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Sample.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Sample.java
index 8336dd6723c2cf41bd99899b0b8b9d59667468b0..6ad7964899bb96c54de3b23d0a22c0f519163e7b 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Sample.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Sample.java
@@ -15,11 +15,7 @@
  */
 package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
 
 import ch.systemsx.cisd.common.reflection.CollectionMapping;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IAttachmentHolder;
@@ -81,6 +77,8 @@ public final class Sample extends CodeWithRegistrationAndModificationDate<Sample
 
     private Collection<Metaproject> metaprojects;
 
+    private Map<String, String> metaData;
+
     public Sample()
     {
         this(false);
@@ -259,6 +257,17 @@ public final class Sample extends CodeWithRegistrationAndModificationDate<Sample
         this.project = project;
     }
 
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
+
     //
     // IIdentifierHolder
     //
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleType.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleType.java
index bf3064e1517243c37ff2a0f53f455031726abb62..bf2b4fbea04b02a948f858662ada7549d3db473f 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleType.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleType.java
@@ -16,11 +16,7 @@
 package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
 
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 import ch.systemsx.cisd.common.reflection.CollectionMapping;
 
@@ -49,6 +45,8 @@ public final class SampleType extends EntityType implements Serializable
 
     private String generatedCodePrefix;
 
+    private Map<String, String> metaData;
+
     private List<SampleTypePropertyType> sampleTypePropertyTypes =
             new ArrayList<SampleTypePropertyType>(0);
 
@@ -169,6 +167,16 @@ public final class SampleType extends EntityType implements Serializable
         this.generatedCodePrefix = generatedCodePrefix;
     }
 
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
     //
     // Object
     //
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
index 36d88374613e9b96af5e5df5161ba21b9b6236c5..fa3c9d44b116e4dfc1050ca59466e6990348bff6 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
@@ -44,17 +44,11 @@ import javax.persistence.Version;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
 
+import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.JsonMapUserType;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.hibernate.annotations.BatchSize;
-import org.hibernate.annotations.Cache;
-import org.hibernate.annotations.CacheConcurrencyStrategy;
-import org.hibernate.annotations.Fetch;
-import org.hibernate.annotations.FetchMode;
-import org.hibernate.annotations.Generated;
-import org.hibernate.annotations.GenerationTime;
-import org.hibernate.annotations.OptimisticLock;
+import org.hibernate.annotations.*;
 import org.hibernate.validator.constraints.Length;
 
 import ch.rinn.restrictions.Friend;
@@ -81,6 +75,7 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 @Entity
 @Table(name = TableNames.SAMPLES_VIEW)
 @Friend(toClasses = ProjectPE.class)
+@TypeDefs({ @TypeDef(name = "JsonMap", typeClass = JsonMapUserType.class) })
 public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Comparable<SamplePE>,
         IEntityInformationWithPropertiesHolder, IMatchingEntity, IDeletablePE,
         IEntityWithMetaprojects, IModifierAndModificationDateBean, IIdentityHolder, Serializable
@@ -136,6 +131,8 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
     private Set<MetaprojectAssignmentPE> metaprojectAssignments =
             new HashSet<MetaprojectAssignmentPE>();
 
+    private Map<String, String> metaData;
+
     @OptimisticLock(excluded = true)
     @OneToMany(fetch = FetchType.LAZY, mappedBy = "parentSample")
     @Fetch(FetchMode.SUBSELECT)
@@ -1105,4 +1102,16 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
         this.metaprojectAssignments = metaprojectAssignments;
     }
 
+    @Column(name = "meta_data")
+    @Type(type = "JsonMap")
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
 }
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SampleTypePE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SampleTypePE.java
index b5b425e70e7277c4dce40b0a38bd0a8d3181b4a5..3a7cf1dc127c4c80bb573c1236d70f9b4cd3737f 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SampleTypePE.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SampleTypePE.java
@@ -17,6 +17,7 @@ package ch.systemsx.cisd.openbis.generic.shared.dto;
 
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import javax.persistence.CascadeType;
@@ -34,7 +35,11 @@ import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
 
+import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.JsonMapUserType;
 import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.TypeDefs;
 import org.hibernate.validator.constraints.Length;
 
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
@@ -48,6 +53,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
  */
 @Entity
 @Table(name = TableNames.SAMPLE_TYPES_TABLE, uniqueConstraints = { @UniqueConstraint(columnNames = { ColumnNames.CODE_COLUMN }) })
+@TypeDefs({ @TypeDef(name = "JsonMap", typeClass = JsonMapUserType.class) })
 public final class SampleTypePE extends EntityTypePE
 {
     private static final long serialVersionUID = IServer.VERSION;
@@ -73,6 +79,8 @@ public final class SampleTypePE extends EntityTypePE
 
     private String generatedCodePrefix;
 
+    private Map<String, String> metaData;
+
     @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityTypeInternal", orphanRemoval = true)
     private Set<SampleTypePropertyTypePE> getSampleTypePropertyTypesInternal()
     {
@@ -231,4 +239,16 @@ public final class SampleTypePE extends EntityTypePE
         return getSampleTypePropertyTypes();
     }
 
+    @Column(name = "meta_data")
+    @Type(type = "JsonMap")
+    public Map<String, String> getMetaData()
+    {
+        return metaData;
+    }
+
+    public void setMetaData(Map<String, String> metaData)
+    {
+        this.metaData = metaData;
+    }
+
 }
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
index b27b344493f4d902d4f5aca01e4521a6b60c816a..b571c575bbd62e7a5fdce9eb2952ffc8c6977a2c 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTranslator.java
@@ -165,6 +165,7 @@ public final class SampleTranslator
                         AttachmentTranslator.translate(samplePE.getAttachments(), baseIndexURL);
             }
             result.setAttachments(attachments);
+            result.setMetaData(samplePE.getMetaData());
         }
         if (containerDep > 0 && samplePE.getContainer() != null)
         {
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTypeTranslator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTypeTranslator.java
index cdba9bcc28a09ce085dc1fe6a8e2fe827116a6ec..e6ac72b3cf1db0283e9d007fa6a5959c1081919e 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTypeTranslator.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/SampleTypeTranslator.java
@@ -65,6 +65,7 @@ public class SampleTypeTranslator
 
         result.setModificationDate(sampleTypePE.getModificationDate());
         result.setValidationScript(ScriptTranslator.translate(sampleTypePE.getValidationScript()));
+        result.setMetaData(sampleTypePE.getMetaData());
         return result;
 
     }
diff --git a/server-application-server/source/sql/generic/192/schema-192.sql b/server-application-server/source/sql/generic/192/schema-192.sql
index 70035d09f00ec373a261786b75d7ff579cbe3e0b..0b7f7daca45152ebc8931217f4217c67d7151068 100644
--- a/server-application-server/source/sql/generic/192/schema-192.sql
+++ b/server-application-server/source/sql/generic/192/schema-192.sql
@@ -33,10 +33,10 @@ CREATE TABLE PERSONS (ID TECH_ID NOT NULL,FIRST_NAME VARCHAR(30),LAST_NAME VARCH
 CREATE TABLE PROJECTS (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL,CODE CODE NOT NULL,SPACE_ID TECH_ID NOT NULL,PERS_ID_LEADER TECH_ID,DESCRIPTION TEXT_VALUE,PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, PERS_ID_MODIFIER TECH_ID, VERSION INTEGER DEFAULT 0, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_EXP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_SAMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', SPACE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F');
 CREATE TABLE PROPERTY_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000 NOT NULL,LABEL COLUMN_LABEL NOT NULL,DATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,COVO_ID TECH_ID,IS_MANAGED_INTERNALLY BOOLEAN_CHAR NOT NULL DEFAULT 'F', MATY_PROP_ID TECH_ID, SATY_PROP_ID TECH_ID, SCHEMA TEXT_VALUE, TRANSFORMATION TEXT_VALUE, META_DATA JSONB);
 CREATE TABLE ROLE_ASSIGNMENTS (ID TECH_ID NOT NULL,ROLE_CODE AUTHORIZATION_ROLE NOT NULL,SPACE_ID TECH_ID, PROJECT_ID TECH_ID, PERS_ID_GRANTEE TECH_ID, AG_ID_GRANTEE TECH_ID, PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP);
-CREATE TABLE SAMPLES_ALL (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL, sample_identifier sample_identifier, CODE CODE NOT NULL, EXPE_ID TECH_ID,SATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,DEL_ID TECH_ID, ORIG_DEL TECH_ID, SPACE_ID TECH_ID, SAMP_ID_PART_OF TECH_ID, PERS_ID_MODIFIER TECH_ID, code_unique_check character varying(300), subcode_unique_check character varying(300), VERSION INTEGER DEFAULT 0, PROJ_ID TECH_ID, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_COMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_CHILDREN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_PARENTS BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_DATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', SPACE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', EXPE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', CONT_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL);
+CREATE TABLE SAMPLES_ALL (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL, sample_identifier sample_identifier, CODE CODE NOT NULL, EXPE_ID TECH_ID,SATY_ID TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP,PERS_ID_REGISTERER TECH_ID NOT NULL,DEL_ID TECH_ID, ORIG_DEL TECH_ID, SPACE_ID TECH_ID, SAMP_ID_PART_OF TECH_ID, PERS_ID_MODIFIER TECH_ID, code_unique_check character varying(300), subcode_unique_check character varying(300), VERSION INTEGER DEFAULT 0, PROJ_ID TECH_ID, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_COMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_CHILDREN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_PARENTS BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_DATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', SPACE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', EXPE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', CONT_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, META_DATA JSONB);
 CREATE TABLE SAMPLE_PROPERTIES (ID TECH_ID NOT NULL,SAMP_ID TECH_ID NOT NULL,STPT_ID TECH_ID NOT NULL,VALUE TEXT_VALUE,CVTE_ID TECH_ID,MATE_PROP_ID TECH_ID,SAMP_PROP_ID TECH_ID,PERS_ID_REGISTERER TECH_ID NOT NULL, REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, PERS_ID_AUTHOR TECH_ID NOT NULL, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, SAMP_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, IS_UNIQUE BOOLEAN_CHAR NOT NULL DEFAULT 'F', INTEGER_ARRAY_VALUE LONG_VALUE[], REAL_ARRAY_VALUE DOUBLE_VALUE[], STRING_ARRAY_VALUE TEXT_VALUE[], TIMESTAMP_ARRAY_VALUE TIME_STAMP[], JSON_VALUE JSONB);
 CREATE TABLE SAMPLE_PROPERTIES_HISTORY (ID TECH_ID NOT NULL, SAMP_ID TECH_ID NOT NULL, STPT_ID TECH_ID NOT NULL, VALUE TEXT_VALUE, VOCABULARY_TERM IDENTIFIER, MATERIAL IDENTIFIER, SAMPLE IDENTIFIER, PERS_ID_AUTHOR TECH_ID NOT NULL, VALID_FROM_TIMESTAMP TIME_STAMP NOT NULL, VALID_UNTIL_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, INTEGER_ARRAY_VALUE LONG_VALUE[], REAL_ARRAY_VALUE DOUBLE_VALUE[], STRING_ARRAY_VALUE TEXT_VALUE[], TIMESTAMP_ARRAY_VALUE TIME_STAMP[], JSON_VALUE JSONB);
-CREATE TABLE SAMPLE_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000, IS_LISTABLE BOOLEAN_CHAR NOT NULL DEFAULT 'T', GENERATED_FROM_DEPTH INTEGER NOT NULL DEFAULT 0, PART_OF_DEPTH INTEGER NOT NULL DEFAULT 0, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, is_auto_generated_code BOOLEAN_CHAR NOT NULL DEFAULT 'F', generated_code_prefix CODE NOT NULL DEFAULT 'S', is_subcode_unique BOOLEAN_CHAR NOT NULL DEFAULT 'F', INHERIT_PROPERTIES BOOLEAN_CHAR NOT NULL DEFAULT 'F', VALIDATION_SCRIPT_ID TECH_ID, SHOW_PARENT_METADATA BOOLEAN_CHAR NOT NULL DEFAULT 'F');
+CREATE TABLE SAMPLE_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000, IS_LISTABLE BOOLEAN_CHAR NOT NULL DEFAULT 'T', GENERATED_FROM_DEPTH INTEGER NOT NULL DEFAULT 0, PART_OF_DEPTH INTEGER NOT NULL DEFAULT 0, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, is_auto_generated_code BOOLEAN_CHAR NOT NULL DEFAULT 'F', generated_code_prefix CODE NOT NULL DEFAULT 'S', is_subcode_unique BOOLEAN_CHAR NOT NULL DEFAULT 'F', INHERIT_PROPERTIES BOOLEAN_CHAR NOT NULL DEFAULT 'F', VALIDATION_SCRIPT_ID TECH_ID, SHOW_PARENT_METADATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', META_DATA JSONB);
 CREATE TABLE SAMPLE_TYPE_PROPERTY_TYPES (ID TECH_ID NOT NULL,SATY_ID TECH_ID NOT NULL,PRTY_ID TECH_ID NOT NULL,IS_MANDATORY BOOLEAN_CHAR NOT NULL DEFAULT 'F',IS_MANAGED_INTERNALLY BOOLEAN_CHAR NOT NULL DEFAULT 'F',PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, IS_DISPLAYED BOOLEAN_CHAR NOT NULL DEFAULT 'T', ORDINAL ORDINAL_INT NOT NULL, SECTION DESCRIPTION_2000,SCRIPT_ID TECH_ID,IS_SHOWN_EDIT BOOLEAN_CHAR NOT NULL DEFAULT 'T',SHOW_RAW_VALUE BOOLEAN_CHAR NOT NULL DEFAULT 'F', IS_UNIQUE BOOLEAN_CHAR NOT NULL DEFAULT 'F');
 
 CREATE TABLE DATA_SET_PROPERTIES (ID TECH_ID NOT NULL,DS_ID TECH_ID NOT NULL,DSTPT_ID TECH_ID NOT NULL,VALUE TEXT_VALUE,CVTE_ID TECH_ID, MATE_PROP_ID TECH_ID, SAMP_PROP_ID TECH_ID, PERS_ID_REGISTERER TECH_ID NOT NULL, REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, PERS_ID_AUTHOR TECH_ID NOT NULL, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, DASE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, IS_UNIQUE BOOLEAN_CHAR NOT NULL DEFAULT 'F', INTEGER_ARRAY_VALUE LONG_VALUE[], REAL_ARRAY_VALUE DOUBLE_VALUE[], STRING_ARRAY_VALUE TEXT_VALUE[], TIMESTAMP_ARRAY_VALUE TIME_STAMP[], JSON_VALUE JSONB);
@@ -154,7 +154,7 @@ CREATE VIEW experiments_deleted AS
 CREATE VIEW samples AS
      SELECT id, perm_id, code, proj_id, proj_frozen, expe_id, expe_frozen, saty_id, registration_timestamp, 
             modification_timestamp, pers_id_registerer, pers_id_modifier, del_id, orig_del, space_id, space_frozen, 
-            samp_id_part_of, cont_frozen, version, frozen, frozen_for_comp, frozen_for_children, frozen_for_parents, frozen_for_data, tsvector_document, sample_identifier
+            samp_id_part_of, cont_frozen, version, frozen, frozen_for_comp, frozen_for_children, frozen_for_parents, frozen_for_data, tsvector_document, sample_identifier, meta_data
        FROM samples_all 
       WHERE del_id IS NULL;
 
diff --git a/server-application-server/source/sql/postgresql/192/function-192.sql b/server-application-server/source/sql/postgresql/192/function-192.sql
index 21a69fc4889383b6108fb779040004bdab9a085a..1839d904dbb232be4284f6668a2409431848f554 100644
--- a/server-application-server/source/sql/postgresql/192/function-192.sql
+++ b/server-application-server/source/sql/postgresql/192/function-192.sql
@@ -612,7 +612,8 @@ CREATE OR REPLACE RULE sample_insert AS
          saty_id,
          space_id,
          space_frozen,
-         version
+         version,
+         meta_data
        ) VALUES (
          NEW.id,
          NEW.frozen,
@@ -637,7 +638,8 @@ CREATE OR REPLACE RULE sample_insert AS
          NEW.saty_id,
          NEW.space_id,
          NEW.space_frozen,
-         NEW.version
+         NEW.version,
+         NEW.meta_data
        );
 
 CREATE OR REPLACE RULE sample_update AS
@@ -665,7 +667,8 @@ CREATE OR REPLACE RULE sample_update AS
               saty_id = NEW.saty_id,
               space_id = NEW.space_id,
               space_frozen = NEW.space_frozen,
-              version = NEW.version
+              version = NEW.version,
+              meta_data = NEW.meta_data
           WHERE id = NEW.id;
 
 CREATE OR REPLACE RULE sample_delete AS