From c26f2b1232f9a887b1680de3ff6eaab57e4e1fbf Mon Sep 17 00:00:00 2001
From: alaskowski <alaskowski@ethz.ch>
Date: Fri, 2 Jun 2023 10:31:01 +0200
Subject: [PATCH] SSDM-13637: Added Metadata field to Sample and SampleType

---
 .../generic/asapi/v3/dto/sample/Sample.java   | 14 ++++
 .../asapi/v3/dto/sample/SampleType.java       | 17 +++++
 .../v3/dto/sample/create/SampleCreation.java  | 12 ++++
 .../dto/sample/create/SampleTypeCreation.java | 13 ++++
 .../dto/sample/update/SampleTypeUpdate.java   | 17 +++++
 .../v3/dto/sample/update/SampleUpdate.java    | 21 ++++--
 .../src/v3/as/dto/sample/Sample.js            | 13 +++-
 .../src/v3/as/dto/sample/SampleType.js        | 13 +++-
 .../v3/as/dto/sample/create/SampleCreation.js |  7 ++
 .../dto/sample/create/SampleTypeCreation.js   | 13 +++-
 .../as/dto/sample/update/SampleTypeUpdate.js  | 17 ++++-
 .../v3/as/dto/sample/update/SampleUpdate.js   | 18 +++--
 .../executor/sample/CreateSampleExecutor.java |  1 +
 .../sample/CreateSampleTypesExecutor.java     |  1 +
 .../executor/sample/UpdateSampleExecutor.java | 71 +++++++++++++++++--
 .../sample/UpdateSampleTypeExecutor.java      | 56 +++++++++++++++
 .../translator/sample/SampleBaseRecord.java   |  2 +
 .../v3/translator/sample/SampleQuery.java     |  5 +-
 .../translator/sample/SampleTranslator.java   |  1 +
 .../sample/SampleTypeBaseRecord.java          |  2 +
 .../sample/SampleTypeTranslator.java          |  2 +
 .../dataaccess/db/DatabaseVersionHolder.java  |  2 +-
 .../generic/shared/basic/dto/Sample.java      | 19 +++--
 .../generic/shared/basic/dto/SampleType.java  | 18 +++--
 .../openbis/generic/shared/dto/SamplePE.java  | 25 ++++---
 .../generic/shared/dto/SampleTypePE.java      | 20 ++++++
 .../shared/translator/SampleTranslator.java   |  1 +
 .../translator/SampleTypeTranslator.java      |  1 +
 .../source/sql/generic/192/schema-192.sql     |  6 +-
 .../sql/postgresql/192/function-192.sql       |  9 ++-
 30 files changed, 370 insertions(+), 47 deletions(-)

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 590c678864c..16201e1b94f 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 a52bb94ab3f..c34d87d9c6d 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 6f97aa030db..91d7b64f3ef 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 c1bbdc54064..6599555ea9c 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 9051aedf87e..cff30959d23 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 cdb005fe94b..063192849d1 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 99695a3495e..23852de463d 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 940a90de026..758e5708d18 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 61e717b3f8e..74dda788162 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 c9017945b17..f33472654da 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 dac4b2366d4..a0d0603e3f8 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 216bd09c018..b542d7d1f89 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 00c15b29cf3..7c21b1e6d53 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 3bf87c8b40b..ee955b767c1 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 089b1d4f529..f8483e7da3d 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 d587dcee175..3a9e1ca6afe 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 aa6087e9fcc..996077f78d9 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 0b766b36f3e..abc5890fe56 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 85237008b97..b97c31bab37 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 57727bfd407..49af9577a3e 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 2d8d2c08af0..f972c5b5700 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 37bc0a60aeb..613f8b5b0ff 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 8336dd6723c..6ad7964899b 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 bf3064e1517..bf2b4fbea04 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 36d88374613..fa3c9d44b11 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 b5b425e70e7..3a7cf1dc127 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 b27b344493f..b571c575bbd 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 cdba9bcc28a..e6ac72b3cf1 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 70035d09f00..0b7f7daca45 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 21a69fc4889..1839d904dbb 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
-- 
GitLab