From 069826c16b0973af1f7989058d6069f72f40aa1a Mon Sep 17 00:00:00 2001
From: alaskowski <alaskowski@ethz.ch>
Date: Mon, 5 Jun 2023 13:54:50 +0200
Subject: [PATCH] SSDM-13637: Added meta_data field to experiment and
 experimentType; amended java and javascript APIs

---
 .../asapi/v3/dto/experiment/Experiment.java   | 16 ++++
 .../v3/dto/experiment/ExperimentType.java     | 17 ++++
 .../experiment/create/ExperimentCreation.java | 12 +++
 .../create/ExperimentTypeCreation.java        | 13 ++++
 .../update/ExperimentTypeUpdate.java          | 17 ++++
 .../experiment/update/ExperimentUpdate.java   | 21 ++++-
 .../src/v3/as/dto/experiment/Experiment.js    | 13 +++-
 .../v3/as/dto/experiment/ExperimentType.js    | 13 +++-
 .../experiment/create/ExperimentCreation.js   | 14 +++-
 .../create/ExperimentTypeCreation.js          | 13 +++-
 .../experiment/update/ExperimentTypeUpdate.js | 16 +++-
 .../dto/experiment/update/ExperimentUpdate.js | 17 +++-
 .../experiment/CreateExperimentExecutor.java  |  1 +
 .../CreateExperimentTypesExecutor.java        |  2 +-
 .../experiment/UpdateExperimentExecutor.java  | 72 +++++++++++++++--
 .../UpdateExperimentTypeExecutor.java         | 61 +++++++++++++++
 .../asapi/v3/helper/generators/Generator.java |  2 +
 .../experiment/ExperimentBaseRecord.java      |  2 +
 .../experiment/ExperimentQuery.java           |  5 +-
 .../experiment/ExperimentTranslator.java      |  2 +
 .../experiment/ExperimentTypeBaseRecord.java  |  2 +
 .../experiment/ExperimentTypeTranslator.java  |  2 +
 .../generic/shared/basic/dto/Experiment.java  | 14 ++++
 .../shared/basic/dto/ExperimentType.java      | 13 ++++
 .../generic/shared/dto/ExperimentPE.java      | 31 ++++----
 .../generic/shared/dto/ExperimentTypePE.java  | 31 +++++---
 .../translator/ExperimentTranslator.java      |  1 +
 .../translator/ExperimentTypeTranslator.java  |  1 +
 .../source/sql/generic/192/schema-192.sql     |  6 +-
 .../sql/postgresql/192/function-192.sql       |  9 ++-
 .../migration/migration-191-192.sql           | 78 +++++++++++++++++++
 31 files changed, 465 insertions(+), 52 deletions(-)

diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
index c40e3996f46..2606c9f8c19 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
@@ -145,6 +145,10 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     @JsonProperty
     private List<Attachment> attachments;
 
+    @JsonProperty
+    private Map<String, String> metaData;
+
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public ExperimentFetchOptions getFetchOptions()
@@ -848,6 +852,18 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         setProperty(propertyName, propertyValue);
     }
 
+    @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/experiment/ExperimentType.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/ExperimentType.java
index eed4b0fbe7d..8e05cf142cd 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/ExperimentType.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/ExperimentType.java
@@ -32,6 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 /*
  * Class automatically generated with DtoGenerator
@@ -62,6 +63,10 @@ public class ExperimentType implements Serializable, ICodeHolder, IDescriptionHo
     @JsonProperty
     private Plugin validationPlugin;
 
+    @JsonProperty
+    private Map<String, String> metaData;
+
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public ExperimentTypeFetchOptions getFetchOptions()
@@ -170,6 +175,18 @@ public class ExperimentType implements Serializable, ICodeHolder, IDescriptionHo
         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/experiment/create/ExperimentCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
index 6097d4802ad..f32fe52a060 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentCreation.java
@@ -54,6 +54,8 @@ public class ExperimentCreation implements ICreation, IObjectCreation, ICreation
 
     private CreationId creationId;
 
+    private Map<String, String> metaData;
+
     public void setTypeId(IEntityTypeId typeId)
     {
         this.typeId = typeId;
@@ -94,6 +96,16 @@ public class ExperimentCreation implements ICreation, IObjectCreation, ICreation
         this.tagIds = tagIds;
     }
 
+    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/experiment/create/ExperimentTypeCreation.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentTypeCreation.java
index 1eb1ac1ea86..f42a67bb43f 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentTypeCreation.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/create/ExperimentTypeCreation.java
@@ -16,6 +16,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.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;
@@ -40,6 +41,8 @@ public class ExperimentTypeCreation implements IEntityTypeCreation
 
     private List<PropertyAssignmentCreation> propertyAssignments;
 
+    private Map<String, String> metaData;
+
     @Override
     public String getCode()
     {
@@ -88,6 +91,16 @@ public class ExperimentTypeCreation 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/experiment/update/ExperimentTypeUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentTypeUpdate.java
index f85fec055b4..926f86f5e83 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentTypeUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentTypeUpdate.java
@@ -17,6 +17,7 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.update;
 
 import java.util.List;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.ListUpdateMapValues;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
@@ -49,6 +50,9 @@ public class ExperimentTypeUpdate implements IEntityTypeUpdate
     @JsonProperty
     private PropertyAssignmentListUpdateValue propertyAssignments = new PropertyAssignmentListUpdateValue();
 
+    @JsonProperty
+    private ListUpdateMapValues metaData = new ListUpdateMapValues();
+
     @Override
     @JsonIgnore
     public IEntityTypeId getObjectId()
@@ -112,6 +116,19 @@ public class ExperimentTypeUpdate 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/experiment/update/ExperimentUpdate.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java
index c577701b7ab..65dfee91b4d 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/update/ExperimentUpdate.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;
@@ -26,10 +27,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 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.experiment.id.IExperimentId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.IProjectId;
@@ -69,6 +66,9 @@ public class ExperimentUpdate implements IUpdate, IObjectUpdate<IExperimentId>,
     @JsonProperty
     private AttachmentListUpdateValue attachments = new AttachmentListUpdateValue();
 
+    @JsonProperty
+    private ListUpdateMapValues metaData = new ListUpdateMapValues();
+
     @Override
     @JsonIgnore
     public IExperimentId getObjectId()
@@ -378,6 +378,19 @@ public class ExperimentUpdate implements IUpdate, IObjectUpdate<IExperimentId>,
         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/experiment/Experiment.js b/api-openbis-javascript/src/v3/as/dto/experiment/Experiment.js
index 92eddf528fd..3cf538a60db 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/Experiment.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/Experiment.js
@@ -34,6 +34,7 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.registrator = null;
 		prototype.modifier = null;
 		prototype.attachments = null;
+		prototype.metaData = null;
 		prototype.getFetchOptions = function() {
 			return this.fetchOptions;
 		};
@@ -374,6 +375,12 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.setAttachments = function(attachments) {
 			this.attachments = attachments;
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaData = function(metaData) {
+            this.metaData = metaData;
+        };
 		prototype.toString = function() {
 			return "Experiment " + this.permId;
 		};
@@ -438,7 +445,11 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		attachments : {
 			name : "List",
 			arguments : [ "Attachment" ]
-		}
+		},
+        metaData: {
+             name: "Map",
+             arguments: ["String", "String"]
+         }
 	});
 	return Experiment;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/experiment/ExperimentType.js b/api-openbis-javascript/src/v3/as/dto/experiment/ExperimentType.js
index cb12eca75ea..565fa375b0d 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/ExperimentType.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/ExperimentType.js
@@ -18,6 +18,7 @@ define(['stjs'], function (stjs) {
       prototype.modificationDate = null
       prototype.propertyAssignments = null
       prototype.validationPlugin = null
+      prototype.metaData = null;
       prototype.getPropertyAssignments = function () {
         if (
           this.getFetchOptions() &&
@@ -78,6 +79,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()
       }
@@ -90,7 +97,11 @@ define(['stjs'], function (stjs) {
         name: 'List',
         arguments: ['PropertyAssignment']
       },
-      validationPlugin: 'Plugin'
+      validationPlugin: 'Plugin',
+      metaData: {
+        name: "Map",
+        arguments: ["String", "String"]
+      }
     }
   )
   return ExperimentType
diff --git a/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentCreation.js b/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentCreation.js
index d5af878a291..dc536cb1a81 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentCreation.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentCreation.js
@@ -15,7 +15,7 @@ define([ "stjs" ], function(stjs) {
 		prototype.properties = null;
 		prototype.attachments = null;
 		prototype.creationId = null;
-
+        prototype.metaData = null;
 		prototype.setTypeId = function(typeId) {
 			this.typeId = typeId;
 		};
@@ -154,6 +154,12 @@ define([ "stjs" ], function(stjs) {
 		prototype.setCreationId = function(creationId) {
 			this.creationId = creationId;
 		};
+		prototype.getMetaData = function() {
+            return this.metaData;
+        };
+        prototype.setMetaData = function(metaData) {
+            this.metaData = metaData;
+        };
 	}, {
 		typeId : "IEntityTypeId",
 		projectId : "IProjectId",
@@ -169,7 +175,11 @@ define([ "stjs" ], function(stjs) {
 			name : "List",
 			arguments : [ "AttachmentCreation" ]
 		},
-		creationId : "CreationId"
+        creationId : "CreationId",
+        metaData: {
+             name: "Map",
+             arguments: ["String", "String"]
+        }
 	});
 	return ExperimentCreation;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentTypeCreation.js b/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentTypeCreation.js
index 56d473bf691..7dabdb02808 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentTypeCreation.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/create/ExperimentTypeCreation.js
@@ -11,6 +11,7 @@ define([ "stjs" ], function(stjs) {
 		prototype.description = null;
 		prototype.validationPluginId = null;
 		prototype.propertyAssignments = null;
+        prototype.metaData = null;
 
 		prototype.getCode = function() {
 			return this.code;
@@ -36,13 +37,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 ExperimentTypeCreation;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentTypeUpdate.js b/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentTypeUpdate.js
index df2b587b195..076b9c98378 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentTypeUpdate.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentTypeUpdate.js
@@ -1,9 +1,11 @@
-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 ExperimentTypeUpdate = function() {
 		this.description = new FieldUpdateValue();
 		this.validationPluginId = new FieldUpdateValue();
 		this.propertyAssignments = new PropertyAssignmentListUpdateValue();
+		this.metaData = new ListUpdateMapValues();
 	};
 	stjs.extend(ExperimentTypeUpdate, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.experiment.update.ExperimentTypeUpdate';
@@ -12,6 +14,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 		prototype.description = null;
 		prototype.validationPluginId = null;
 		prototype.propertyAssignments = null;
+		prototype.metaData = null;
 
 		prototype.getObjectId = function() {
 			return this.getTypeId();
@@ -40,6 +43,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 : {
@@ -50,7 +59,8 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/entitytype/upd
 			name : "FieldUpdateValue",
 			arguments : [ "IPluginId" ]
 		},
-		propertyAssignments : "PropertyAssignmentListUpdateValue"
+		propertyAssignments : "PropertyAssignmentListUpdateValue",
+		metaData : "ListUpdateMapValues"
 	});
 	return ExperimentTypeUpdate;
 })
\ No newline at end of file
diff --git a/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentUpdate.js b/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentUpdate.js
index b34d7714a3a..0deae9fc13f 100644
--- a/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentUpdate.js
+++ b/api-openbis-javascript/src/v3/as/dto/experiment/update/ExperimentUpdate.js
@@ -1,13 +1,16 @@
 /**
  * @author pkupczyk
  */
-define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/IdListUpdateValue", "as/dto/attachment/update/AttachmentListUpdateValue" ], function(stjs, FieldUpdateValue, IdListUpdateValue,
-		AttachmentListUpdateValue) {
+define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/IdListUpdateValue",
+    "as/dto/attachment/update/AttachmentListUpdateValue", "as/dto/common/update/ListUpdateMapValues" ],
+    function(stjs, FieldUpdateValue, IdListUpdateValue,
+		AttachmentListUpdateValue, ListUpdateMapValues) {
 	var ExperimentUpdate = function() {
 		this.properties = {};
 		this.projectId = new FieldUpdateValue();
 		this.tagIds = new IdListUpdateValue();
 		this.attachments = new AttachmentListUpdateValue();
+		this.metaData = new ListUpdateMapValues();
 	};
 	stjs.extend(ExperimentUpdate, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.experiment.update.ExperimentUpdate';
@@ -20,6 +23,7 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 		prototype.projectId = null;
 		prototype.tagIds = null;
 		prototype.attachments = null;
+		prototype.metaData = null;
 
 		prototype.getObjectId = function() {
 			return this.getExperimentId();
@@ -167,6 +171,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);
+        };
 	}, {
 		experimentId : "IExperimentId",
 		properties : {
@@ -181,7 +191,8 @@ define([ "stjs", "as/dto/common/update/FieldUpdateValue", "as/dto/common/update/
 			name : "IdListUpdateValue",
 			arguments : [ "ITagId" ]
 		},
-		attachments : "AttachmentListUpdateValue"
+		attachments : "AttachmentListUpdateValue",
+        metaData : "ListUpdateMapValues"
 	});
 	return ExperimentUpdate;
 })
\ No newline at end of file
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
index bbb97b4d0b7..06b78045911 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentExecutor.java
@@ -101,6 +101,7 @@ public class CreateExperimentExecutor extends AbstractCreateEntityExecutor<Exper
                     experiment.setPermId(createdPermId);
                     experiment.setRegistrator(person);
                     RelationshipUtils.updateModificationDateAndModifier(experiment, person, timeStamp);
+                    experiment.setMetaData(object.getMetaData());
                     experiments.add(experiment);
                 }
 
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentTypesExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentTypesExecutor.java
index edf5470e548..a451eef136b 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentTypesExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/CreateExperimentTypesExecutor.java
@@ -64,7 +64,7 @@ public class CreateExperimentTypesExecutor extends AbstractCreateEntityTypeExecu
     @Override
     protected void fillTypeSpecificFields(ExperimentType type, ExperimentTypeCreation creation)
     {
-        // nothing to do
+        type.setMetaData(creation.getMetaData());
     }
 
     @Override
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
index 6958cc94660..af3397f4c99 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentExecutor.java
@@ -15,13 +15,11 @@
  */
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment;
 
-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;
@@ -114,6 +112,7 @@ public class UpdateExperimentExecutor extends AbstractUpdateEntityExecutor<Exper
         updateExperimentPropertyExecutor.update(context, batch);
         updateTags(context, batch);
         updateAttachments(context, batch);
+        updateMetaData(context, batch);
 
         PersonPE person = context.getSession().tryGetPerson();
         Date timeStamp = daoFactory.getTransactionTimestamp();
@@ -156,6 +155,69 @@ public class UpdateExperimentExecutor extends AbstractUpdateEntityExecutor<Exper
         }
     }
 
+    private void updateMetaData(final IOperationContext context, final MapBatch<ExperimentUpdate, ExperimentPE> batch)
+    {
+        new MapBatchProcessor<ExperimentUpdate, ExperimentPE>(context, batch)
+        {
+            @Override
+            public void process(ExperimentUpdate update, ExperimentPE 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(ExperimentUpdate update, ExperimentPE entity, int objectIndex, int totalObjectCount)
+            {
+                return new UpdateRelationProgress(update, entity, "experiment-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())
+                    {
+                        metaDataChanged.set(true);
+                        metaData.putAll(map);
+                    }
+                }
+            }
+        };
+    }
+
     private void updateTags(final IOperationContext context, final MapBatch<ExperimentUpdate, ExperimentPE> batch)
     {
         new MapBatchProcessor<ExperimentUpdate, ExperimentPE>(context, batch)
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentTypeExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentTypeExecutor.java
index 7943c226ac0..1f3df9a824d 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentTypeExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/experiment/UpdateExperimentTypeExecutor.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.experiment;
 
+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.ExperimentTypePE;
 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;
+
 /**
  * @author Franz-Josef Elmer
  */
@@ -51,6 +57,12 @@ public class UpdateExperimentTypeExecutor
     {
     }
 
+    @Override
+    protected void updateSpecific(ExperimentTypePE type, ExperimentTypeUpdate update)
+    {
+        updateMetaData(type, update);
+    }
+
     @Override
     protected IUpdateEntityTypePropertyTypesExecutor<ExperimentTypeUpdate, ExperimentTypePE> getUpdateEntityTypePropertyTypeExecutor()
     {
@@ -63,4 +75,53 @@ public class UpdateExperimentTypeExecutor
         authorizationExecutor.canUpdate(context);
     }
 
+    private void updateMetaData(ExperimentTypePE type, ExperimentTypeUpdate 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/helper/generators/Generator.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
index 1dadf4b81bd..ba929dd1517 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/generators/Generator.java
@@ -341,6 +341,7 @@ public class Generator extends AbstractGenerator
         addRegistrator(gen);
         addModifier(gen);
         addAttachments(gen);
+        addMetaData(gen);
 
         gen.setToStringMethod("\"Experiment \" + permId");
 
@@ -356,6 +357,7 @@ public class Generator extends AbstractGenerator
         addCode(gen);
         addDescription(gen);
         addModificationDate(gen);
+        addMetaData(gen);
 
         gen.setToStringMethod("\"ExperimentType \" + code");
         addPropertyAssignments(gen);
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentBaseRecord.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentBaseRecord.java
index f8d441c51be..c4fe53eb592 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentBaseRecord.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentBaseRecord.java
@@ -43,4 +43,6 @@ public class ExperimentBaseRecord 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/experiment/ExperimentQuery.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java
index c13492e0059..1a8be90db0c 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java
@@ -38,7 +38,8 @@ public interface ExperimentQuery extends ObjectQuery
 
     @Select(sql = "select e.id, e.code, e.perm_id as permId, p.code as projectCode, sp.code as spaceCode, "
             + "e.registration_timestamp as registrationDate, e.modification_timestamp as modificationDate, "
-            + "e.frozen as frozen, e.frozen_for_data as frozenForDataSets, e.frozen_for_samp as frozenForSamples "
+            + "e.frozen as frozen, e.frozen_for_data as frozenForDataSets, e.frozen_for_samp as frozenForSamples, "
+            + "e.meta_data as metaData "
             + "from experiments e join projects p on e.proj_id = p.id "
             + "join spaces sp on p.space_id = sp.id "
             + "where e.id = any(?{1})", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
@@ -48,7 +49,7 @@ public interface ExperimentQuery extends ObjectQuery
             LongSetMapper.class }, fetchSize = FETCH_SIZE)
     public List<ObjectRelationRecord> getTypeIds(LongSet experimentIds);
 
-    @Select(sql = "select et.id, et.code, et.description, et.modification_timestamp as modificationDate "
+    @Select(sql = "select et.id, et.code, et.description, et.modification_timestamp as modificationDate, et.meta_data as metaData "
             + "from experiment_types et where et.id = any(?{1})", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
     public List<ExperimentTypeBaseRecord> getTypes(LongSet experimentTypeIds);
 
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTranslator.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTranslator.java
index 7edefd0ad6c..f74e934c10c 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTranslator.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTranslator.java
@@ -19,6 +19,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.CommonUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -234,6 +235,7 @@ public class ExperimentTranslator extends AbstractCachingTranslator<Long, Experi
         result.setFrozenForSamples(baseRecord.frozenForSamples);
         result.setRegistrationDate(baseRecord.registrationDate);
         result.setModificationDate(baseRecord.modificationDate);
+        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/experiment/ExperimentTypeBaseRecord.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeBaseRecord.java
index 8d0d441b651..45fb11f3e67 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeBaseRecord.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeBaseRecord.java
@@ -31,4 +31,6 @@ public class ExperimentTypeBaseRecord 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/experiment/ExperimentTypeTranslator.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeTranslator.java
index dd98602196d..f7ff3c1e9c2 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeTranslator.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentTypeTranslator.java
@@ -18,6 +18,7 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.experiment;
 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;
 
@@ -86,6 +87,7 @@ public class ExperimentTypeTranslator extends AbstractCachingTranslator<Long, Ex
         result.setCode(baseRecord.code);
         result.setDescription(baseRecord.description);
         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/shared/basic/dto/Experiment.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Experiment.java
index 7be474770d5..329f6ce1327 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Experiment.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/Experiment.java
@@ -17,6 +17,7 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.IAttachmentHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithProperties;
@@ -61,6 +62,8 @@ public class Experiment extends CodeWithRegistrationAndModificationDate<Experime
 
     private Collection<Metaproject> metaprojects;
 
+    private Map<String, String> metaData;
+
     public Experiment()
     {
         this(false);
@@ -237,4 +240,15 @@ public class Experiment extends CodeWithRegistrationAndModificationDate<Experime
     {
         this.metaprojects = metaprojects;
     }
+
+    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/basic/dto/ExperimentType.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentType.java
index ce346505c32..a1099b1212e 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentType.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentType.java
@@ -16,6 +16,7 @@
 package ch.systemsx.cisd.openbis.generic.shared.basic.dto;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * The <i>GWT</i> equivalent to ExperimentTypePE.
@@ -28,6 +29,8 @@ public class ExperimentType extends EntityType
 
     private List<ExperimentTypePropertyType> experimentTypePropertyTypes;
 
+    private Map<String, String> metaData;
+
     @Override
     public List<ExperimentTypePropertyType> getAssignedPropertyTypes()
     {
@@ -51,4 +54,14 @@ public class ExperimentType extends EntityType
     {
         return EntityKind.EXPERIMENT;
     }
+
+    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/ExperimentPE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
index 6c5142f3dd5..e64c51d641d 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java
@@ -16,11 +16,7 @@
 package ch.systemsx.cisd.openbis.generic.shared.dto;
 
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
@@ -43,17 +39,11 @@ import javax.validation.constraints.Pattern;
 import ch.systemsx.cisd.openbis.generic.server.CommonServiceProvider;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleDAO;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+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;
@@ -79,6 +69,7 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 @Table(name = TableNames.EXPERIMENTS_VIEW, uniqueConstraints = {
         @UniqueConstraint(columnNames = { ColumnNames.CODE_COLUMN, ColumnNames.PROJECT_COLUMN }) })
 @Friend(toClasses = { AttachmentPE.class, ProjectPE.class })
+@TypeDefs({ @TypeDef(name = "JsonMap", typeClass = JsonMapUserType.class) })
 public class ExperimentPE extends AttachmentHolderPE implements
         IEntityInformationWithPropertiesHolder, IIdAndCodeHolder, Comparable<ExperimentPE>,
         IModifierAndModificationDateBean, IMatchingEntity, IDeletablePE, IEntityWithMetaprojects, IIdentityHolder,
@@ -153,6 +144,8 @@ public class ExperimentPE extends AttachmentHolderPE implements
 
     private String permId;
 
+    private Map<String, String> metaData;
+
     @Column(name = ColumnNames.REGISTRATION_TIMESTAMP_COLUMN, nullable = false, insertable = false, updatable = false)
     @Generated(GenerationTime.INSERT)
     public Date getRegistrationDate()
@@ -709,4 +702,16 @@ public class ExperimentPE extends AttachmentHolderPE implements
     {
         setSamples(samples);
     }
+
+    @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/ExperimentTypePE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentTypePE.java
index e9615b5bad5..3fd5eac4263 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentTypePE.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentTypePE.java
@@ -17,22 +17,18 @@ 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;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
+import javax.persistence.*;
 import javax.persistence.Id;
-import javax.persistence.OneToMany;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
-import javax.persistence.Transient;
-import javax.persistence.UniqueConstraint;
 
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
+import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.JsonMapUserType;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+import org.hibernate.annotations.TypeDefs;
 
 /**
  * Persistence entity representing type of experiment.
@@ -42,6 +38,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
  */
 @Entity
 @Table(name = TableNames.EXPERIMENT_TYPES_TABLE, uniqueConstraints = { @UniqueConstraint(columnNames = { ColumnNames.CODE_COLUMN }) })
+@TypeDefs({ @TypeDef(name = "JsonMap", typeClass = JsonMapUserType.class) })
 public final class ExperimentTypePE extends EntityTypePE
 {
     private static final long serialVersionUID = IServer.VERSION;
@@ -49,6 +46,8 @@ public final class ExperimentTypePE extends EntityTypePE
     private Set<ExperimentTypePropertyTypePE> exerimentTypePropertyTypes =
             new HashSet<ExperimentTypePropertyTypePE>();
 
+    private Map<String, String> metaData;
+
     @Override
     @SequenceGenerator(name = SequenceNames.EXPERIMENT_TYPE_SEQUENCE, sequenceName = SequenceNames.EXPERIMENT_TYPE_SEQUENCE, allocationSize = 1)
     @Id
@@ -113,4 +112,16 @@ public final class ExperimentTypePE extends EntityTypePE
         return getExperimentTypePropertyTypes();
     }
 
+    @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/ExperimentTranslator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTranslator.java
index f321a8218eb..feeeaa8efd2 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTranslator.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTranslator.java
@@ -144,6 +144,7 @@ public final class ExperimentTranslator
         result.setModifier(PersonTranslator.translate(experiment.getModifier()));
         result.setDeletion(DeletionTranslator.translate(experiment.getDeletion()));
         result.setVersion(experiment.getVersion());
+        result.setMetaData(experiment.getMetaData());
         for (final LoadableFields field : withFields)
         {
             switch (field)
diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTypeTranslator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTypeTranslator.java
index e3fcfd28cfe..2e1fd0670de 100644
--- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTypeTranslator.java
+++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExperimentTypeTranslator.java
@@ -47,6 +47,7 @@ public class ExperimentTypeTranslator
         result.setModificationDate(experimentTypePE.getModificationDate());
         result.setValidationScript(ScriptTranslator.translate(experimentTypePE
                 .getValidationScript()));
+        result.setMetaData(experimentTypePE.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 0b7f7daca45..a36235c47cf 100644
--- a/server-application-server/source/sql/generic/192/schema-192.sql
+++ b/server-application-server/source/sql/generic/192/schema-192.sql
@@ -10,12 +10,12 @@ CREATE TABLE DATA_STORE_SERVICE_DATA_SET_TYPES (DATA_STORE_SERVICE_ID TECH_ID NO
 CREATE TABLE DATA_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000 NOT NULL);
 CREATE TABLE EVENTS (ID TECH_ID NOT NULL,EVENT_TYPE EVENT_TYPE NOT NULL,DESCRIPTION TEXT_VALUE,REASON DESCRIPTION_2000,PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, entity_type VARCHAR(80) NOT NULL, identifiers TEXT_VALUE NOT NULL, CONTENT TEXT_VALUE, EXAC_ID TECH_ID);
 CREATE TABLE EVENTS_SEARCH (ID TECH_ID NOT NULL, EVENT_TYPE EVENT_TYPE NOT NULL, ENTITY_TYPE TEXT_VALUE NOT NULL, ENTITY_SPACE TEXT_VALUE, ENTITY_SPACE_PERM_ID TEXT_VALUE, ENTITY_PROJECT TEXT_VALUE, ENTITY_PROJECT_PERM_ID TEXT_VALUE, ENTITY_REGISTERER TEXT_VALUE, ENTITY_REGISTRATION_TIMESTAMP TIME_STAMP, IDENTIFIER TEXT_VALUE NOT NULL, DESCRIPTION TEXT_VALUE, REASON TEXT_VALUE, CONTENT TEXT_VALUE, EXAC_ID TECH_ID, PERS_ID_REGISTERER TECH_ID NOT NULL, REGISTRATION_TIMESTAMP TIME_STAMP NOT NULL);
-CREATE TABLE EXPERIMENTS_ALL (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL,CODE CODE NOT NULL,EXTY_ID TECH_ID NOT NULL,PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, PROJ_ID TECH_ID NOT NULL,DEL_ID TECH_ID, ORIG_DEL TECH_ID, IS_PUBLIC BOOLEAN_CHAR NOT NULL DEFAULT 'F', PERS_ID_MODIFIER TECH_ID, VERSION INTEGER DEFAULT 0, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_SAMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_DATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL);
+CREATE TABLE EXPERIMENTS_ALL (ID TECH_ID NOT NULL,PERM_ID CODE NOT NULL,CODE CODE NOT NULL,EXTY_ID TECH_ID NOT NULL,PERS_ID_REGISTERER TECH_ID NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, PROJ_ID TECH_ID NOT NULL,DEL_ID TECH_ID, ORIG_DEL TECH_ID, IS_PUBLIC BOOLEAN_CHAR NOT NULL DEFAULT 'F', PERS_ID_MODIFIER TECH_ID, VERSION INTEGER DEFAULT 0, FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_SAMP BOOLEAN_CHAR NOT NULL DEFAULT 'F', FROZEN_FOR_DATA BOOLEAN_CHAR NOT NULL DEFAULT 'F', PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', TSVECTOR_DOCUMENT TSVECTOR NOT NULL, META_DATA JSONB);
 CREATE TABLE ATTACHMENTS (ID TECH_ID NOT NULL,EXPE_ID TECH_ID,SAMP_ID TECH_ID,PROJ_ID TECH_ID,EXAC_ID TECH_ID NOT NULL,FILE_NAME FILE_NAME NOT NULL,REGISTRATION_TIMESTAMP TIME_STAMP_DFL NOT NULL DEFAULT CURRENT_TIMESTAMP,VERSION INTEGER NOT NULL,PERS_ID_REGISTERER TECH_ID NOT NULL, title TITLE_100, description DESCRIPTION_2000, PROJ_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', EXPE_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F', SAMP_FROZEN BOOLEAN_CHAR NOT NULL DEFAULT 'F');
 CREATE TABLE ATTACHMENT_CONTENTS (ID TECH_ID NOT NULL,VALUE FILE NOT NULL);
 CREATE TABLE EXPERIMENT_PROPERTIES (ID TECH_ID NOT NULL,EXPE_ID TECH_ID NOT NULL,ETPT_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, EXPE_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 EXPERIMENT_PROPERTIES_HISTORY (ID TECH_ID NOT NULL, EXPE_ID TECH_ID NOT NULL, ETPT_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 EXPERIMENT_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, VALIDATION_SCRIPT_ID TECH_ID);
+CREATE TABLE EXPERIMENT_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000, MODIFICATION_TIMESTAMP TIME_STAMP DEFAULT CURRENT_TIMESTAMP, VALIDATION_SCRIPT_ID TECH_ID, META_DATA JSONB);
 CREATE TABLE EXPERIMENT_TYPE_PROPERTY_TYPES (ID TECH_ID NOT NULL,EXTY_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, 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 EXTERNAL_DATA (ID TECH_ID NOT NULL,SHARE_ID CODE,SIZE ORDINAL_INT,LOCATION VARCHAR(1024) NOT NULL,FFTY_ID TECH_ID NOT NULL,LOTY_ID TECH_ID NOT NULL,CVTE_ID_STOR_FMT TECH_ID NOT NULL,IS_COMPLETE BOOLEAN_CHAR_OR_UNKNOWN NOT NULL DEFAULT 'U',CVTE_ID_STORE TECH_ID, STATUS ARCHIVING_STATUS NOT NULL DEFAULT 'AVAILABLE', PRESENT_IN_ARCHIVE BOOLEAN_CHAR DEFAULT 'F', SPEED_HINT INTEGER NOT NULL DEFAULT -50, STORAGE_CONFIRMATION BOOLEAN_CHAR NOT NULL DEFAULT 'F', H5_FOLDERS BOOLEAN_CHAR NOT NULL, H5AR_FOLDERS BOOLEAN_CHAR NOT NULL, ARCHIVING_REQUESTED BOOLEAN_CHAR NOT NULL DEFAULT 'F');
 CREATE TABLE FILE_FORMAT_TYPES (ID TECH_ID NOT NULL,CODE CODE NOT NULL,DESCRIPTION DESCRIPTION_2000);
@@ -142,7 +142,7 @@ CREATE VIEW data_deleted AS
 
 CREATE VIEW experiments AS
      SELECT id, perm_id, code, exty_id, pers_id_registerer, pers_id_modifier, registration_timestamp, modification_timestamp, 
-            proj_id, proj_frozen, del_id, orig_del, is_public, version, frozen, frozen_for_samp, frozen_for_data, tsvector_document
+            proj_id, proj_frozen, del_id, orig_del, is_public, version, frozen, frozen_for_samp, frozen_for_data, tsvector_document, meta_data
        FROM experiments_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 1839d904dbb..4245d212817 100644
--- a/server-application-server/source/sql/postgresql/192/function-192.sql
+++ b/server-application-server/source/sql/postgresql/192/function-192.sql
@@ -713,7 +713,8 @@ CREATE OR REPLACE RULE experiment_insert AS
        proj_id,
        proj_frozen,
        registration_timestamp,
-       version
+       version,
+       meta_data
      ) VALUES (
        NEW.id,
        NEW.frozen,
@@ -731,7 +732,8 @@ CREATE OR REPLACE RULE experiment_insert AS
        NEW.proj_id,
        NEW.proj_frozen,
        NEW.registration_timestamp,
-       NEW.version
+       NEW.version,
+       NEW.meta_data
      );
 
 CREATE OR REPLACE RULE experiment_update AS
@@ -752,7 +754,8 @@ CREATE OR REPLACE RULE experiment_update AS
               proj_id = NEW.proj_id,
               proj_frozen = NEW.proj_frozen,
               registration_timestamp = NEW.registration_timestamp,
-              version = NEW.version
+              version = NEW.version,
+              meta_data = NEW.meta_data
           WHERE id = NEW.id;
 
 CREATE OR REPLACE RULE experiment_delete AS
diff --git a/server-application-server/source/sql/postgresql/migration/migration-191-192.sql b/server-application-server/source/sql/postgresql/migration/migration-191-192.sql
index 6f74d1ec943..e3d5e405e95 100644
--- a/server-application-server/source/sql/postgresql/migration/migration-191-192.sql
+++ b/server-application-server/source/sql/postgresql/migration/migration-191-192.sql
@@ -16,6 +16,19 @@ CREATE OR REPLACE VIEW samples AS
      WHERE del_id IS NULL;
 
 
+ALTER TABLE IF EXISTS EXPERIMENTS_ALL
+    ADD COLUMN META_DATA jsonb;
+
+ALTER TABLE IF EXISTS EXPERIMENT_TYPES
+    ADD COLUMN META_DATA jsonb;
+
+CREATE OR REPLACE VIEW experiments AS
+     SELECT id, perm_id, code, exty_id, pers_id_registerer, pers_id_modifier, registration_timestamp, modification_timestamp,
+            proj_id, proj_frozen, del_id, orig_del, is_public, version, frozen, frozen_for_samp, frozen_for_data, tsvector_document, meta_data
+       FROM experiments_all
+      WHERE del_id IS NULL;
+
+
 -- function
 
 CREATE OR REPLACE RULE sample_insert AS
@@ -102,3 +115,68 @@ CREATE OR REPLACE RULE sample_update AS
               version = NEW.version,
               meta_data = NEW.meta_data
           WHERE id = NEW.id;
+
+
+CREATE OR REPLACE RULE experiment_insert AS
+  ON INSERT TO experiments DO INSTEAD
+     INSERT INTO experiments_all (
+       id,
+       frozen,
+       frozen_for_samp,
+       frozen_for_data,
+       code,
+       del_id,
+       orig_del,
+       exty_id,
+       is_public,
+       modification_timestamp,
+       perm_id,
+       pers_id_registerer,
+       pers_id_modifier,
+       proj_id,
+       proj_frozen,
+       registration_timestamp,
+       version,
+       meta_data
+     ) VALUES (
+       NEW.id,
+       NEW.frozen,
+       NEW.frozen_for_samp,
+       NEW.frozen_for_data,
+       NEW.code,
+       NEW.del_id,
+       NEW.orig_del,
+       NEW.exty_id,
+       NEW.is_public,
+       NEW.modification_timestamp,
+       NEW.perm_id,
+       NEW.pers_id_registerer,
+       NEW.pers_id_modifier,
+       NEW.proj_id,
+       NEW.proj_frozen,
+       NEW.registration_timestamp,
+       NEW.version,
+       NEW.meta_data
+     );
+
+CREATE OR REPLACE RULE experiment_update AS
+    ON UPDATE TO experiments DO INSTEAD
+       UPDATE experiments_all
+          SET code = NEW.code,
+              frozen = NEW.frozen,
+              frozen_for_samp = NEW.frozen_for_samp,
+              frozen_for_data = NEW.frozen_for_data,
+              del_id = NEW.del_id,
+              orig_del = NEW.orig_del,
+              exty_id = NEW.exty_id,
+              is_public = NEW.is_public,
+              modification_timestamp = NEW.modification_timestamp,
+              perm_id = NEW.perm_id,
+              pers_id_registerer = NEW.pers_id_registerer,
+              pers_id_modifier = NEW.pers_id_modifier,
+              proj_id = NEW.proj_id,
+              proj_frozen = NEW.proj_frozen,
+              registration_timestamp = NEW.registration_timestamp,
+              version = NEW.version,
+              meta_data = NEW.meta_data
+          WHERE id = NEW.id;
-- 
GitLab