trace-server: Create experiment in workspace and detect conflicts

Create an experiment resource in the workspace's Tracing project. Add
its trace resources and create its supplementary folder. Set the
experiment type and trace types.

Detect a conflict when an experiment with the same name exists with a
different set of traces. Detect the conflict even if the experiment is
not opened.

Create the trace resource using the given trace name, not the last
segment of the given path. Detect a conflict when a trace with the same
name exists in the same parent folder but with a different target
location (path) or a different trace type.

Posting a trace only creates the trace in the workspace and detects its
trace type. A trace instance is not created. Trace instances are created
only when the experiment is posted.

Delete the experiment's resources and supplementary folder when the
experiment is deleted.

Delete the traces resources and supplementary folder when a
trace is deleted only if the trace is not used in any remaining
experiment. Otherwise reject the trace deletion.

Assign a fixed UUID based on the trace path and name in
TraceManagerService.
Assign a fixed UUID based on the experiment name in
ExperimentManagerService.

Fix the endpoints in DataProviderService to use expUUID instead of uuid,
according to TSP specification. Get the experiment instance from the
ExperimentManagerService.

Create new classes Trace and Experiment for the JSon model. This is
required in order to use the trace server assigned trace UUID and
experiment UUID in TSP response, instead of the instance's getUUID().

Update unit tests to use experiments when appropriate according to TSP
specification.

Change-Id: Ic749939e4e26a3e9f3ac8d625fa0d173b5334801
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass.incubator/org.eclipse.tracecompass.incubator/+/170789
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/DataProviderServiceTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/DataProviderServiceTest.java
index ef045da..be6b2a7 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/DataProviderServiceTest.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/DataProviderServiceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018-2020 Ericsson and others
+ * Copyright (c) 2018, 2020 Ericsson and others
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -35,6 +35,7 @@
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.DataProviderDescriptorStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryModelStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryStub;
+import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.LineModelStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableColumnsOutputResponseStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TableLinesOutputResponseStub;
@@ -74,12 +75,10 @@
      */
     @Test
     public void testProviders() {
-
-        WebTarget traces = getApplicationEndpoint().path(TRACES);
-        RestServerTest.assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+        ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
 
         WebTarget experiments = getApplicationEndpoint().path(EXPERIMENTS);
-        WebTarget providers = experiments.path(CONTEXT_SWITCHES_UST_UUID.toString())
+        WebTarget providers = experiments.path(exp.getUUID().toString())
                 .path(OUTPUTS_PATH);
 
         Set<DataProviderDescriptorStub> descriptors = getDataProviderDescriptors(providers);
@@ -93,10 +92,9 @@
      */
     @Test
     public void testCallStackDataProvider() {
-        WebTarget traces = getApplicationEndpoint().path(TRACES);
-        RestServerTest.assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+        ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
 
-        WebTarget callstackTree = getTimeGraphTreeEndpoint(CONTEXT_SWITCHES_UST_UUID.toString(), CALL_STACK_DATAPROVIDER_ID);
+        WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
 
         Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0L, Long.MAX_VALUE, 2));
         Response tree = callstackTree.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
@@ -121,11 +119,10 @@
         long start = 1412670961211260539L;
         long end = 1412670967217750839L;
         try {
-            WebTarget traces = getApplicationEndpoint().path(TRACES);
-            RestServerTest.assertPost(traces, ARM_64_KERNEL_STUB);
+            ExperimentModelStub exp = assertPostExperiment(ARM_64_KERNEL_STUB.getName(), ARM_64_KERNEL_STUB);
 
             // Test getting the tree endpoint for an XY chart
-            WebTarget xyTree = getXYTreeEndpoint(ARM_64_KERNEL_UUID.toString(), XY_DATAPROVIDER_ID);
+            WebTarget xyTree = getXYTreeEndpoint(exp.getUUID().toString(), XY_DATAPROVIDER_ID);
 
             Map<String, Object> parameters = new HashMap<>();
             parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, ImmutableList.of(start, end));
@@ -154,7 +151,7 @@
             assertFalse(entries.isEmpty());
 
             // Test getting the XY series endpoint
-            WebTarget xySeriesEnpoint = getXYSeriesEndpoint(ARM_64_KERNEL_UUID.toString(), XY_DATAPROVIDER_ID);
+            WebTarget xySeriesEnpoint = getXYSeriesEndpoint(exp.getUUID().toString(), XY_DATAPROVIDER_ID);
             List<Integer> items = new ArrayList<>();
             for (EntryStub entry : entries) {
                 items.add(entry.getId());
@@ -191,11 +188,10 @@
         long start = 1450193697034689597L;
         long end = 1450193745774189602L;
         try {
-            WebTarget traces = getApplicationEndpoint().path(TRACES);
-            RestServerTest.assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+            ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_UST_STUB.getName(), CONTEXT_SWITCHES_UST_STUB);
 
             // Test getting the time graph tree
-            WebTarget callstackTree = getTimeGraphTreeEndpoint(CONTEXT_SWITCHES_UST_UUID.toString(), CALL_STACK_DATAPROVIDER_ID);
+            WebTarget callstackTree = getTimeGraphTreeEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
 
             Map<String, Object> parameters = new HashMap<>();
             TgTreeOutputResponseStub responseModel;
@@ -230,7 +226,7 @@
             }
 
             // Test getting the time graph row data
-            WebTarget tgStatesEnpoint = getTimeGraphStatesEndpoint(CONTEXT_SWITCHES_UST_UUID.toString(), CALL_STACK_DATAPROVIDER_ID);
+            WebTarget tgStatesEnpoint = getTimeGraphStatesEndpoint(exp.getUUID().toString(), CALL_STACK_DATAPROVIDER_ID);
             parameters.put(DataProviderParameterUtils.REQUESTED_ITEMS_KEY, items);
             Response statesResponse = tgStatesEnpoint.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
             assertEquals("There should be a positive response for the data provider", 200, statesResponse.getStatus());
@@ -264,11 +260,10 @@
         long start = 1412670961211260539L;
         long end = 1412670967217750839L;
         try {
-            WebTarget traces = getApplicationEndpoint().path(TRACES);
-            RestServerTest.assertPost(traces, ARM_64_KERNEL_STUB);
+            ExperimentModelStub exp = assertPostExperiment(ARM_64_KERNEL_STUB.getName(), ARM_64_KERNEL_STUB);
 
             // Test getting the tree endpoint for an XY chart
-            WebTarget tableColumns = getTableColumnsEndpoint(ARM_64_KERNEL_UUID.toString(), EVENTS_TABLE_DATAPROVIDER_ID);
+            WebTarget tableColumns = getTableColumnsEndpoint(exp.getUUID().toString(), EVENTS_TABLE_DATAPROVIDER_ID);
 
             Map<String, Object> parameters = new HashMap<>();
             parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, ImmutableList.of(start, end));
@@ -296,7 +291,7 @@
             assertFalse(columns.isEmpty());
 
             // Test getting the XY series endpoint
-            WebTarget tableLinesEnpoint = getTableLinesEndpoint(ARM_64_KERNEL_UUID.toString(), EVENTS_TABLE_DATAPROVIDER_ID);
+            WebTarget tableLinesEnpoint = getTableLinesEndpoint(exp.getUUID().toString(), EVENTS_TABLE_DATAPROVIDER_ID);
             List<Long> requestedColumnsIds = new ArrayList<>();
             for (int i = 0; i <= columns.size() / 2; i++) {
                 requestedColumnsIds.add(columns.get(i).getId());
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/ExperimentManagerServiceTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/ExperimentManagerServiceTest.java
index 315bd4f..908ee2c 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/ExperimentManagerServiceTest.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/ExperimentManagerServiceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -18,7 +18,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
@@ -43,16 +42,8 @@
 public class ExperimentManagerServiceTest extends RestServerTest {
 
     private static final String TEST = "test";
-    private static final @NonNull String EXPERIMENT_UUID = "bb12687f-9866-3a9f-bf55-b4d9da0137ed";
-    private static final @NonNull String EXPERIMENT2_UUID = "b3050981-d846-3ff4-95b0-af8342c519c5";
-    private static final @NonNull String EXPERIMENT3_UUID = "5466b39b-6f53-3aa7-98da-0b9992e6bae6";
     private static final @NonNull ImmutableSet<TraceModelStub> CONTEXT_SWITCH_SET = ImmutableSet.of(CONTEXT_SWITCHES_KERNEL_STUB, CONTEXT_SWITCHES_UST_STUB);
-    private static final ExperimentModelStub EXPECTED = new ExperimentModelStub(TEST,
-            UUID.fromString(EXPERIMENT_UUID), 0L, 0L, 0L, "RUNNING", CONTEXT_SWITCH_SET);
-    private static final ExperimentModelStub EXPECTED2 = new ExperimentModelStub(TEST,
-            UUID.fromString(EXPERIMENT2_UUID), 0L, 0L, 0L, "RUNNING", ImmutableSet.of(CONTEXT_SWITCHES_KERNEL_STUB));
-    private static final ExperimentModelStub EXPECTED3 = new ExperimentModelStub(TEST,
-            UUID.fromString(EXPERIMENT3_UUID), 0L, 0L, 0L, "RUNNING", ImmutableSet.of(ARM_64_KERNEL_STUB, CONTEXT_SWITCHES_UST_STUB));
+    private static final @NonNull ExperimentModelStub EXPECTED = new ExperimentModelStub(TEST, CONTEXT_SWITCH_SET);
 
     /**
      * Basic test for the {@link ExperimentManagerService}
@@ -63,27 +54,28 @@
         WebTarget traces = application.path(TRACES);
         WebTarget expTarget = application.path(EXPERIMENTS);
 
-        assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
-        assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        TraceModelStub ustStub = assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+        TraceModelStub kernelStub = assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
         assertEquals(CONTEXT_SWITCH_SET, getTraces(traces));
 
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
 
         List<String> traceUUIDs = new ArrayList<>();
-        traceUUIDs.add(CONTEXT_SWITCHES_KERNEL_UUID.toString());
-        traceUUIDs.add(CONTEXT_SWITCHES_UST_UUID.toString());
+        traceUUIDs.add(ustStub.getUUID().toString());
+        traceUUIDs.add(kernelStub.getUUID().toString());
 
         Map<String, Object> parameters = new HashMap<>();
         parameters.put(NAME, EXPECTED.getName());
         parameters.put(TRACES, traceUUIDs);
 
         Response response = expTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
-        assertEquals("Failed to POST the experiment", EXPECTED, response.readEntity(ExperimentModelStub.class));
+        ExperimentModelStub expStub = response.readEntity(ExperimentModelStub.class);
+        assertEquals("Failed to POST the experiment", EXPECTED, expStub);
         assertEquals("Failed to add experiment to set of experiments", Collections.singleton(EXPECTED), getExperiments(expTarget));
         assertEquals("Adding an experiment should not change the trace set", CONTEXT_SWITCH_SET, getTraces(traces));
-        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
 
-        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().delete().readEntity(ExperimentModelStub.class));
+        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().delete().readEntity(ExperimentModelStub.class));
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
         assertEquals("Deleting an experiment should not change the trace set", CONTEXT_SWITCH_SET, getTraces(traces));
         response.close();
@@ -98,37 +90,39 @@
         WebTarget traces = application.path(TRACES);
         WebTarget expTarget = application.path(EXPERIMENTS);
 
-        assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
-        assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
-        assertEquals(CONTEXT_SWITCH_SET, getTraces(traces));
+        TraceModelStub ustStub = assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+        TraceModelStub kernelStub = assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        assertEquals(null, CONTEXT_SWITCH_SET, getTraces(traces));
 
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
 
         List<String> traceUUIDs = new ArrayList<>();
-        traceUUIDs.add(CONTEXT_SWITCHES_KERNEL_UUID.toString());
-        traceUUIDs.add(CONTEXT_SWITCHES_UST_UUID.toString());
+        traceUUIDs.add(ustStub.getUUID().toString());
+        traceUUIDs.add(kernelStub.getUUID().toString());
 
         Map<String, Object> parameters = new HashMap<>();
         parameters.put(NAME, EXPECTED.getName());
         parameters.put(TRACES, traceUUIDs);
 
         Response response = expTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
-        assertEquals("Failed to POST the experiment", EXPECTED, response.readEntity(ExperimentModelStub.class));
+        ExperimentModelStub expStub = response.readEntity(ExperimentModelStub.class);
+        assertEquals("Failed to POST the experiment", EXPECTED, expStub);
         assertEquals("Failed to add experiment to set of experiments", Collections.singleton(EXPECTED), getExperiments(expTarget));
         assertEquals("Adding an experiment should not change the trace set", CONTEXT_SWITCH_SET, getTraces(traces));
-        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
         response.close();
 
         // Make a second post with the same name and traces, should return the experiment
         Response response2 = expTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
+        ExperimentModelStub expStub2 = response2.readEntity(ExperimentModelStub.class);
         assertEquals("Status of second post", Status.OK.getStatusCode(), response2.getStatus());
-        assertEquals("Failed to POST the experiment a second time", EXPECTED, response2.readEntity(ExperimentModelStub.class));
+        assertEquals("Failed to POST the experiment a second time", EXPECTED, expStub2);
         assertEquals("There should still be only one experiment", Collections.singleton(EXPECTED), getExperiments(expTarget));
         assertEquals("Adding an experiment should not change the trace set", CONTEXT_SWITCH_SET, getTraces(traces));
-        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub2.getUUID().toString()).request().get(ExperimentModelStub.class));
         response2.close();
 
-        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().delete().readEntity(ExperimentModelStub.class));
+        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().delete().readEntity(ExperimentModelStub.class));
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
         assertEquals("Deleting an experiment should not change the trace set", CONTEXT_SWITCH_SET, getTraces(traces));
 
@@ -143,61 +137,60 @@
         WebTarget traces = application.path(TRACES);
         WebTarget expTarget = application.path(EXPERIMENTS);
 
-        assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
-        assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
-        assertPost(traces, ARM_64_KERNEL_STUB);
+        TraceModelStub ustStub = assertPost(traces, CONTEXT_SWITCHES_UST_STUB);
+        TraceModelStub kernelStub = assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        TraceModelStub arm64Stub = assertPost(traces, ARM_64_KERNEL_STUB);
         ImmutableSet<TraceModelStub> traceSet = ImmutableSet.of(CONTEXT_SWITCHES_UST_STUB, CONTEXT_SWITCHES_KERNEL_STUB, ARM_64_KERNEL_STUB);
-        assertEquals(traceSet, getTraces(traces));
+        assertEquals(null, traceSet, getTraces(traces));
 
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
 
         List<String> traceUUIDs = new ArrayList<>();
-        traceUUIDs.add(CONTEXT_SWITCHES_KERNEL_UUID.toString());
-        traceUUIDs.add(CONTEXT_SWITCHES_UST_UUID.toString());
+        traceUUIDs.add(kernelStub.getUUID().toString());
+        traceUUIDs.add(ustStub.getUUID().toString());
 
         Map<String, Object> parameters = new HashMap<>();
         parameters.put(NAME, EXPECTED.getName());
         parameters.put(TRACES, traceUUIDs);
 
         Response response = expTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
-        assertEquals("Failed to POST the experiment", EXPECTED, response.readEntity(ExperimentModelStub.class));
+        ExperimentModelStub expStub = response.readEntity(ExperimentModelStub.class);
+        assertEquals("Failed to POST the experiment", EXPECTED, expStub);
         assertEquals("Failed to add experiment to set of experiments", Collections.singleton(EXPECTED), getExperiments(expTarget));
         assertEquals("Adding an experiment should not change the trace set", traceSet, getTraces(traces));
-        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
         response.close();
 
-        // Post same name experiment, but different trace size, should return an OK status with a new experiment
+        // Post same name experiment, but different traces, should return a conflict
         List<String> traceUUIDs2 = new ArrayList<>();
-        traceUUIDs2.add(CONTEXT_SWITCHES_KERNEL_UUID.toString());
+        traceUUIDs2.add(kernelStub.getUUID().toString());
         Map<String, Object> parameters2 = new HashMap<>();
         parameters2.put(NAME, EXPECTED.getName());
         parameters2.put(TRACES, traceUUIDs2);
         Response response2 = expTarget.request().post(Entity.json(new QueryParameters(parameters2, Collections.emptyList())));
-        assertEquals("Expected a success for posting different experiment", Status.OK.getStatusCode(), response2.getStatus());
-        assertEquals("Conflict should return different experiment", EXPECTED2, response2.readEntity(ExperimentModelStub.class));
-        assertEquals("There should now be two experiments", ImmutableSet.of(EXPECTED, EXPECTED2), getExperiments(expTarget));
-        assertEquals("Adding a new experiment should not change the trace set", traceSet, getTraces(traces));
-        assertEquals("Failed to get the new experiment by its UUID", EXPECTED2, expTarget.path(EXPERIMENT2_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Expected a conflict for posting different experiment", Status.CONFLICT.getStatusCode(), response2.getStatus());
+        assertEquals("Conflict should return original experiment name", EXPECTED.getName(), response2.readEntity(ExperimentModelStub.class).getName());
+        assertEquals("There should still be only one experiment", ImmutableSet.of(EXPECTED), getExperiments(expTarget));
+        assertEquals("Failing to add an experiment should not change the trace set", traceSet, getTraces(traces));
+        assertEquals("Failed to get the experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
         response2.close();
 
         // Post same experiment name, but with traces with the same names, but not the same traces
         List<String> traceUUIDs3 = new ArrayList<>();
-        traceUUIDs3.add(ARM_64_KERNEL_UUID.toString());
-        traceUUIDs3.add(CONTEXT_SWITCHES_UST_UUID.toString());
+        traceUUIDs3.add(arm64Stub.getUUID().toString());
+        traceUUIDs3.add(ustStub.getUUID().toString());
         Map<String, Object> parameters3 = new HashMap<>();
         parameters3.put(NAME, EXPECTED.getName());
         parameters3.put(TRACES, traceUUIDs3);
         Response response3 = expTarget.request().post(Entity.json(new QueryParameters(parameters3, Collections.emptyList())));
-        assertEquals("Expected a success for posting different experiment", Status.OK.getStatusCode(), response3.getStatus());
-        assertEquals("Conflict should return different experiment", EXPECTED3, response3.readEntity(ExperimentModelStub.class));
-        assertEquals("There should now be two experiments", ImmutableSet.of(EXPECTED, EXPECTED2, EXPECTED3), getExperiments(expTarget));
-        assertEquals("Adding a new experiment should not change the trace set", traceSet, getTraces(traces));
-        assertEquals("Failed to get the new experiment by its UUID", EXPECTED3, expTarget.path(EXPERIMENT3_UUID).request().get(ExperimentModelStub.class));
+        assertEquals("Expected a conflict for posting different experiment", Status.CONFLICT.getStatusCode(), response3.getStatus());
+        assertEquals("Conflict should return original experiment name", EXPECTED.getName(), response3.readEntity(ExperimentModelStub.class).getName());
+        assertEquals("There should still be only one experiment", ImmutableSet.of(EXPECTED), getExperiments(expTarget));
+        assertEquals("Failing to add an experiment should not change the trace set", traceSet, getTraces(traces));
+        assertEquals("Failed to get the new experiment by its UUID", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().get(ExperimentModelStub.class));
         response3.close();
 
-        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(EXPERIMENT_UUID).request().delete().readEntity(ExperimentModelStub.class));
-        assertEquals("Failed to DELETE the experiment", EXPECTED2, expTarget.path(EXPERIMENT2_UUID).request().delete().readEntity(ExperimentModelStub.class));
-        assertEquals("Failed to DELETE the experiment", EXPECTED3, expTarget.path(EXPERIMENT3_UUID).request().delete().readEntity(ExperimentModelStub.class));
+        assertEquals("Failed to DELETE the experiment", EXPECTED, expTarget.path(expStub.getUUID().toString()).request().delete().readEntity(ExperimentModelStub.class));
         assertEquals("experiment set should be empty at this point", Collections.emptySet(), getExperiments(expTarget));
         assertEquals("Deleting an experiment should not change the trace set", traceSet, getTraces(traces));
     }
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/TraceManagerServiceTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/TraceManagerServiceTest.java
index 4d8fa64..ec30a9c 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/TraceManagerServiceTest.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/TraceManagerServiceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -43,14 +43,14 @@
 
         assertTrue("Expected empty set of traces", getTraces(traces).isEmpty());
 
-        assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        TraceModelStub kernelStub = assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
 
-        assertEquals(CONTEXT_SWITCHES_KERNEL_STUB, traces.path(CONTEXT_SWITCHES_KERNEL_UUID.toString()).request().get(TraceModelStub.class));
+        assertEquals(CONTEXT_SWITCHES_KERNEL_STUB, traces.path(kernelStub.getUUID().toString()).request().get(TraceModelStub.class));
 
         assertEquals("Expected set of traces to contain trace2 stub",
                 Collections.singleton(CONTEXT_SWITCHES_KERNEL_STUB), getTraces(traces));
 
-        Response deleteResponse = traces.path(CONTEXT_SWITCHES_KERNEL_UUID.toString()).request().delete();
+        Response deleteResponse = traces.path(kernelStub.getUUID().toString()).request().delete();
         int deleteCode = deleteResponse.getStatus();
         assertEquals("Failed to DELETE trace2, error code=" + deleteCode, 200, deleteCode);
         assertEquals(CONTEXT_SWITCHES_KERNEL_STUB, deleteResponse.readEntity(TraceModelStub.class));
@@ -80,6 +80,7 @@
         WebTarget traces = getApplicationEndpoint().path(TRACES);
 
         assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        assertEquals(ImmutableSet.of(CONTEXT_SWITCHES_KERNEL_STUB), getTraces(traces));
 
         // Post the trace a second time
         assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/XmlManagerServiceTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/XmlManagerServiceTest.java
index e2b6ad0..c503e53 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/XmlManagerServiceTest.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/XmlManagerServiceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -34,6 +34,7 @@
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.XmlManagerService;
+import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub;
 import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest;
 import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
 import org.junit.Test;
@@ -80,10 +81,9 @@
         assertEquals("valid XML should have posted successfully",
                 Collections.singleton("test_valid.xml"), map.keySet());
 
-        WebTarget traces = application.path("traces");
-        assertPost(traces, CONTEXT_SWITCHES_KERNEL_STUB);
+        ExperimentModelStub exp = assertPostExperiment(CONTEXT_SWITCHES_KERNEL_STUB.getName(), CONTEXT_SWITCHES_KERNEL_STUB);
 
-        WebTarget xmlProviderPath = getXYTreeEndpoint(CONTEXT_SWITCHES_KERNEL_UUID.toString(), "org.eclipse.linuxtools.tmf.analysis.xml.core.tests.xy");
+        WebTarget xmlProviderPath = getXYTreeEndpoint(exp.getUUID().toString(), "org.eclipse.linuxtools.tmf.analysis.xml.core.tests.xy");
         Map<String, Object> parameters = new HashMap<>();
         parameters.put(DataProviderParameterUtils.REQUESTED_TIME_KEY, Collections.emptyList());
         Response xmlTree = xmlProviderPath.request().post(Entity.json(new QueryParameters(parameters , Collections.emptyList())));
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ExperimentModelStub.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ExperimentModelStub.java
index ebd7b7c..be98d65 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ExperimentModelStub.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ExperimentModelStub.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -11,6 +11,7 @@
 
 package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs;
 
+import java.nio.charset.Charset;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
@@ -37,7 +38,7 @@
      * {@link JsonCreator} Constructor for final fields
      *
      * @param name
-     *            trace name
+     *            experiment name
      * @param uuid
      *            the stub's UUID
      * @param nbEvents
@@ -50,7 +51,8 @@
      *            encapsulated traces
      */
     @JsonCreator
-    public ExperimentModelStub(@JsonProperty("name") String name,
+    public ExperimentModelStub(
+            @JsonProperty("name") String name,
             @JsonProperty("UUID") UUID uuid,
             @JsonProperty("nbEvents") long nbEvents,
             @JsonProperty("start") long start,
@@ -62,6 +64,22 @@
     }
 
     /**
+     * Constructor for comparing equality
+     *
+     * @param name
+     *            experiment name
+     * @param traces
+     *            encapsulated traces
+     */
+    public ExperimentModelStub(String name, Set<TraceModelStub> traces) {
+        this(name, getUUID(name),  0, 0L, 0L, "RUNNING", traces);
+    }
+
+    private static UUID getUUID(String name) {
+        return UUID.nameUUIDFromBytes(Objects.requireNonNull(name.getBytes(Charset.defaultCharset())));
+    }
+
+    /**
      * Getter for the list of traces in the experiment
      *
      * @return list of traces encapsulated in the experiment
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/TraceModelStub.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/TraceModelStub.java
index be2f874..e9eafd8 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/TraceModelStub.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/TraceModelStub.java
@@ -11,6 +11,7 @@
 
 package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs;
 
+import java.nio.charset.Charset;
 import java.util.Objects;
 import java.util.UUID;
 
@@ -51,7 +52,8 @@
      *            end time
      */
     @JsonCreator
-    public TraceModelStub(@JsonProperty("name") String name,
+    public TraceModelStub(
+            @JsonProperty("name") String name,
             @JsonProperty("path") String path,
             @JsonProperty("UUID") UUID uuid,
             @JsonProperty("nbEvents") long nbEvents,
@@ -69,11 +71,13 @@
      *            trace name
      * @param path
      *            path to trace on server file system
-     * @param uuid
-     *            the stub's UUID
      */
-    public TraceModelStub(String name, String path, UUID uuid) {
-        this(name, path, uuid, 0, 0L, 0L, "RUNNING");
+    public TraceModelStub(String name, String path) {
+        this(name, path, getUUID(path, name), 0, 0L, 0L, "RUNNING");
+    }
+
+    private static UUID getUUID(String path, String name) {
+        return UUID.nameUUIDFromBytes(Objects.requireNonNull((path + name).getBytes(Charset.defaultCharset())));
     }
 
     /**
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/utils/RestServerTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/utils/RestServerTest.java
index 2fc3d8a..158c713 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/utils/RestServerTest.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/utils/RestServerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -14,12 +14,12 @@
 import static org.junit.Assert.assertEquals;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.UUID;
 
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
@@ -126,30 +126,18 @@
     };
 
     /**
-     * {@link UUID} for {@link CtfTestTrace#CONTEXT_SWITCHES_UST}.
-     */
-    protected static final UUID CONTEXT_SWITCHES_UST_UUID = UUID.fromString("8160c5b3-c482-4d86-9d81-3272e872537f");
-    /**
      * {@link TraceModelStub} to represent the object returned by the server for
      * {@link CtfTestTrace#CONTEXT_SWITCHES_UST}.
      */
     protected static TraceModelStub CONTEXT_SWITCHES_UST_STUB;
 
     /**
-     * {@link UUID} for {@link CtfTestTrace#CONTEXT_SWITCHES_KERNEL}.
-     */
-    protected static final UUID CONTEXT_SWITCHES_KERNEL_UUID = UUID.fromString("5694cebc-b3d1-2d46-a2e6-c6993919ae4f");
-    /**
      * {@link TraceModelStub} to represent the object returned by the server for
      * {@link CtfTestTrace#CONTEXT_SWITCHES_KERNEL}.
      */
     protected static TraceModelStub CONTEXT_SWITCHES_KERNEL_STUB;
 
     /**
-     * {@link UUID} for {@link CtfTestTrace#CONTEXT_SWITCHES_KERNEL}.
-     */
-    protected static final UUID ARM_64_KERNEL_UUID = UUID.fromString("5d079bb9-28f0-6648-8bcf-390bb7f31523");
-    /**
      * {@link TraceModelStub} to represent the object returned by the server for
      * {@link CtfTestTrace#ARM_64_BIT_HEADER}, with the same name as {@link #CONTEXT_SWITCHES_KERNEL_STUB}
      */
@@ -168,14 +156,15 @@
      */
     @BeforeClass
     public static void beforeTest() throws IOException {
-        String contextSwitchesUstPath = FileLocator.toFileURL(CtfTestTrace.CONTEXT_SWITCHES_UST.getTraceURL()).getPath();
-        CONTEXT_SWITCHES_UST_STUB = new TraceModelStub("ust", contextSwitchesUstPath, CONTEXT_SWITCHES_UST_UUID);
+        String contextSwitchesUstPath = FileLocator.toFileURL(CtfTestTrace.CONTEXT_SWITCHES_UST.getTraceURL()).getPath().replaceAll("/$", "");
+        CONTEXT_SWITCHES_UST_STUB = new TraceModelStub("ust", contextSwitchesUstPath);
 
-        String contextSwitchesKernelPath = FileLocator.toFileURL(CtfTestTrace.CONTEXT_SWITCHES_KERNEL.getTraceURL()).getPath();
-        CONTEXT_SWITCHES_KERNEL_STUB = new TraceModelStub("kernel", contextSwitchesKernelPath, CONTEXT_SWITCHES_KERNEL_UUID);
+        String contextSwitchesKernelPath = FileLocator.toFileURL(CtfTestTrace.CONTEXT_SWITCHES_KERNEL.getTraceURL()).getPath().replaceAll("/$", "");
+        CONTEXT_SWITCHES_KERNEL_STUB = new TraceModelStub("kernel", contextSwitchesKernelPath);
 
-        String arm64Path = FileLocator.toFileURL(CtfTestTrace.ARM_64_BIT_HEADER.getTraceURL()).getPath();
-        ARM_64_KERNEL_STUB = new TraceModelStub("kernel", arm64Path, ARM_64_KERNEL_UUID);
+        String arm64Path = FileLocator.toFileURL(CtfTestTrace.ARM_64_BIT_HEADER.getTraceURL()).getPath().replaceAll("/$", "");
+        ARM_64_KERNEL_STUB = new TraceModelStub("kernel", arm64Path);
+
         ImmutableList.Builder<DataProviderDescriptorStub> b = ImmutableList.builder();
         b.add(new DataProviderDescriptorStub("org.eclipse.tracecompass.internal.analysis.timing.core.segmentstore.scatter.dataprovider:org.eclipse.linuxtools.lttng2.ust.analysis.callstack",
                 "LTTng-UST CallStack - Latency vs Time",
@@ -236,15 +225,15 @@
     /**
      * Get the {@link WebTarget} for the table columns endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The time graph tree endpoint
      */
-    public static WebTarget getTableColumnsEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getTableColumnsEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(TABLE_PATH)
                 .path(dataProviderId)
@@ -254,15 +243,15 @@
     /**
      * Get the {@link WebTarget} for the table lines endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The time graph tree endpoint
      */
-    public static WebTarget getTableLinesEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getTableLinesEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(TABLE_PATH)
                 .path(dataProviderId)
@@ -272,15 +261,15 @@
     /**
      * Get the {@link WebTarget} for the time graph tree endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The time graph tree endpoint
      */
-    public static WebTarget getTimeGraphTreeEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getTimeGraphTreeEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(TIMEGRAPH_PATH)
                 .path(dataProviderId)
@@ -290,15 +279,15 @@
     /**
      * Get the {@link WebTarget} for the time graph state endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The time graph state endpoint
      */
-    public static WebTarget getTimeGraphStatesEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getTimeGraphStatesEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(TIMEGRAPH_PATH)
                 .path(dataProviderId)
@@ -308,15 +297,15 @@
     /**
      * Get the {@link WebTarget} for the XY tree endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The XY tree endpoint
      */
-    public static WebTarget getXYTreeEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getXYTreeEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(XY_PATH)
                 .path(dataProviderId)
@@ -326,15 +315,15 @@
     /**
      * Get the {@link WebTarget} for the XY series endpoint.
      *
-     * @param UUID
-     *            Trace or experiment UUID
+     * @param expUUID
+     *            Experiment UUID
      * @param dataProviderId
      *            Data provider ID
      * @return The XY series endpoint
      */
-    public static WebTarget getXYSeriesEndpoint(String UUID, String dataProviderId) {
+    public static WebTarget getXYSeriesEndpoint(String expUUID, String dataProviderId) {
         return getApplicationEndpoint().path(EXPERIMENTS)
-                .path(UUID)
+                .path(expUUID)
                 .path(OUTPUTS_PATH)
                 .path(XY_PATH)
                 .path(dataProviderId)
@@ -382,14 +371,43 @@
      *            traces endpoint
      * @param stub
      *            expected trace stub
+     * @return the resulting stub
      */
-    public static void assertPost(WebTarget traces, TraceModelStub stub) {
+    public static TraceModelStub assertPost(WebTarget traces, TraceModelStub stub) {
         Map<String, Object> parameters = new HashMap<>();
         parameters.put(NAME, stub.getName());
         parameters.put(URI, stub.getPath());
         Response response = traces.request().post(Entity.json(new QueryParameters(parameters , Collections.emptyList())));
         int code = response.getStatus();
         assertEquals("Failed to POST " + stub.getName() + ", error code=" + code, 200, code);
-        assertEquals(stub, response.readEntity(TraceModelStub.class));
+        TraceModelStub result = response.readEntity(TraceModelStub.class);
+        assertEquals(stub, result);
+        return result;
+    }
+
+    /**
+     * Post an experiment from a list of {@link TraceModelStub}, ensure that the
+     * post returned correctly and that the returned model was that of the
+     * expected stub.
+     *
+     * @param name
+     *            experiment name
+     * @param traces
+     *            traces to include in experiment
+     * @return the resulting experiment stub
+     */
+    public static ExperimentModelStub assertPostExperiment(String name, TraceModelStub... traces) {
+        WebTarget application = getApplicationEndpoint();
+        List<String> traceUUIDs = new ArrayList<>();
+        for (TraceModelStub trace : traces) {
+            TraceModelStub traceStub = assertPost(application.path(TRACES), trace);
+            traceUUIDs.add(traceStub.getUUID().toString());
+        }
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put(NAME, name);
+        parameters.put(TRACES, traceUUIDs);
+        Response response = application.path(EXPERIMENTS).request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList())));
+        assertEquals("Failed to POST experiment " + name + ", error code=" + response.getStatus(), 200, response.getStatus());
+        return response.readEntity(ExperimentModelStub.class);
     }
 }
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/META-INF/MANIFEST.MF b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/META-INF/MANIFEST.MF
index f23d8b0..baeb859 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/META-INF/MANIFEST.MF
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/META-INF/MANIFEST.MF
@@ -43,6 +43,7 @@
  com.google.common.base,
  com.google.common.collect,
  com.google.common.primitives,
+ org.apache.commons.io,
  org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module,
  org.eclipse.tracecompass.tmf.analysis.xml.core.module
 Bundle-ClassPath: .,
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java
index 6bc2b92..a8a4d41 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/DataProviderService.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017, 2019 Ericsson
+ * Copyright (c) 2017, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -80,6 +80,7 @@
 import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
 import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
 import org.w3c.dom.Element;
 
 import com.google.common.collect.Iterables;
@@ -90,7 +91,7 @@
  * @author Loic Prieur-Drevon
  */
 @SuppressWarnings("restriction")
-@Path("/experiments/{uuid}/outputs")
+@Path("/experiments/{expUUID}/outputs")
 public class DataProviderService {
     private static final String WRONG_PARAMETERS = "Wrong query parameters"; //$NON-NLS-1$
     private static final String NO_PROVIDER = "Analysis cannot run"; //$NON-NLS-1$
@@ -104,20 +105,20 @@
     /**
      * Getter for the list of data provider descriptions
      *
-     * @param uuid
-     *            UUID of the trace to search for
+     * @param expUUID
+     *            UUID of the experiment to search for
      * @return the data provider descriptions with the queried {@link UUID} if it exists.
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getProviders(@PathParam("uuid") UUID uuid) {
-        ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-        if (trace == null) {
+    public Response getProviders(@PathParam("expUUID") UUID expUUID) {
+        TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+        if (experiment == null) {
             return Response.status(Status.NOT_FOUND).build();
         }
-        List<IDataProviderDescriptor> list = DataProviderManager.getInstance().getAvailableProviders(trace);
-        list.addAll(getXmlDataProviderDescriptors(trace, EnumSet.of(OutputType.TIME_GRAPH)));
-        list.addAll(getXmlDataProviderDescriptors(trace, EnumSet.of(OutputType.XY)));
+        List<IDataProviderDescriptor> list = DataProviderManager.getInstance().getAvailableProviders(experiment);
+        list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.TIME_GRAPH)));
+        list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.XY)));
 
         return Response.ok(list).build();
     }
@@ -125,8 +126,8 @@
     /**
      * Getter for the list of data provider descriptions
      *
-     * @param uuid
-     *            UUID of the trace to search for
+     * @param expUUID
+     *            UUID of the experiment to search for
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @return the data provider descriptions with the queried {@link UUID} if it exists.
@@ -134,14 +135,14 @@
     @GET
     @Path("/{outputId}")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getProvider(@PathParam("uuid") UUID uuid, @PathParam("outputId") String outputId) {
-        ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-        if (trace == null) {
+    public Response getProvider(@PathParam("expUUID") UUID expUUID, @PathParam("outputId") String outputId) {
+        TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+        if (experiment == null) {
             return Response.status(Status.NOT_FOUND).build();
         }
-        List<IDataProviderDescriptor> list = DataProviderManager.getInstance().getAvailableProviders(trace);
-        list.addAll(getXmlDataProviderDescriptors(trace, EnumSet.of(OutputType.TIME_GRAPH)));
-        list.addAll(getXmlDataProviderDescriptors(trace, EnumSet.of(OutputType.XY)));
+        List<IDataProviderDescriptor> list = DataProviderManager.getInstance().getAvailableProviders(experiment);
+        list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.TIME_GRAPH)));
+        list.addAll(getXmlDataProviderDescriptors(experiment, EnumSet.of(OutputType.XY)));
 
         Optional<IDataProviderDescriptor> provider = list.stream().filter(p -> p.getId().equals(outputId)).findFirst();
 
@@ -155,8 +156,8 @@
     /**
      * Query the provider for the XY tree
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -168,16 +169,16 @@
     @Path("/XY/{outputId}/tree")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getXYTree(@PathParam("uuid") UUID uuid,
+    public Response getXYTree(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId, QueryParameters queryParameters) {
-        return getTree(uuid, outputId, queryParameters);
+        return getTree(expUUID, outputId, queryParameters);
     }
 
     /**
      * Query the provider for the XY view
      *
-     * @param uuid
-     *            {@link UUID} of the trace to query
+     * @param expUUID
+     *            {@link UUID} of the experiment to query
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -188,24 +189,24 @@
     @Path("/XY/{outputId}/xy")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getXY(@PathParam("uuid") UUID uuid,
+    public Response getXY(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId, QueryParameters queryParameters) {
         if (outputId == null) {
             return Response.status(Status.PRECONDITION_FAILED).entity(MISSING_OUTPUTID).build();
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getXY") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfTreeXYDataProvider<@NonNull ITmfTreeDataModel> provider = manager.getDataProvider(trace,
+            ITmfTreeXYDataProvider<@NonNull ITmfTreeDataModel> provider = manager.getDataProvider(experiment,
                     outputId, ITmfTreeXYDataProvider.class);
 
             if (provider == null) {
                 // try and find the XML provider for the ID.
-                provider = getXmlProvider(trace, outputId, EnumSet.of(OutputType.XY));
+                provider = getXmlProvider(experiment, outputId, EnumSet.of(OutputType.XY));
             }
 
             if (provider == null) {
@@ -226,8 +227,8 @@
     /**
      * Query the provider for XY tooltip, currently not implemented
      *
-     * @param uuid
-     *            {@link UUID} of the trace to query
+     * @param expUUID
+     *            {@link UUID} of the experiment to query
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param xValue
@@ -242,7 +243,7 @@
     @GET
     @Path("/XY/{outputId}/tooltip")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getXYTooltip(@PathParam("uuid") UUID uuid,
+    public Response getXYTooltip(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             @QueryParam("xValue") long xValue,
             @QueryParam("yValue") long yValue,
@@ -253,8 +254,8 @@
     /**
      * Query the provider for the time graph tree
      *
-     * @param uuid
-     *            {@link UUID} of the trace to query
+     * @param expUUID
+     *            {@link UUID} of the experiment to query
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -266,17 +267,17 @@
     @Path("/timeGraph/{outputId}/tree")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getTimeGraphTree(@PathParam("uuid") UUID uuid,
+    public Response getTimeGraphTree(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
-        return getTree(uuid, outputId, queryParameters);
+        return getTree(expUUID, outputId, queryParameters);
     }
 
     /**
      * Query the provider for the time graph states
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -288,7 +289,7 @@
     @Path("/timeGraph/{outputId}/states")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getStates(@PathParam("uuid") UUID uuid,
+    public Response getStates(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
         if (outputId == null) {
@@ -296,12 +297,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getStates") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(trace, outputId);
+            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(experiment, outputId);
 
             if (provider == null) {
                 // The analysis cannot be run on this trace
@@ -321,8 +322,8 @@
     /**
      * Query the provider for the time graph arrows
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -334,7 +335,7 @@
     @Path("/timeGraph/{outputId}/arrows")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getArrows(@PathParam("uuid") UUID uuid,
+    public Response getArrows(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
         if (outputId == null) {
@@ -342,12 +343,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getArrows") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(trace, outputId);
+            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(experiment, outputId);
 
             if (provider == null) {
                 // The analysis cannot be run on this trace
@@ -366,8 +367,8 @@
 
     /**
      * Query the provider for all annotation categories
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @return {@link TmfModelResponse} containing {@link AnnotationCategoriesModel}
@@ -375,7 +376,7 @@
     @GET
     @Path("/{outputId}/annotations")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getAnnotationCategories(@PathParam("uuid") UUID uuid,
+    public Response getAnnotationCategories(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId) {
 
         if (outputId == null) {
@@ -383,12 +384,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getAnnotationCategories") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(trace,
+            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(experiment,
                     outputId, ITmfTreeDataProvider.class);
 
             if (provider == null) {
@@ -409,8 +410,8 @@
 
     /**
      * Query the provider for all annotations for the time range defined by Query parameters
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -422,7 +423,7 @@
     @Path("/{outputId}/annotations")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getAnnotations(@PathParam("uuid") UUID uuid,
+    public Response getAnnotations(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
 
@@ -431,12 +432,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getAnnotations") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(trace,
+            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(experiment,
                     outputId, ITmfTreeDataProvider.class);
 
             if (provider == null) {
@@ -457,8 +458,8 @@
     /**
      * Query the provider for the time graph tooltips
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param time
@@ -472,7 +473,7 @@
     @GET
     @Path("/timeGraph/{outputId}/tooltip")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getTimeGraphTooltip(@PathParam("uuid") UUID uuid,
+    public Response getTimeGraphTooltip(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             @QueryParam("time") long time,
             @QueryParam("entryId") long entryId,
@@ -482,12 +483,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getTimeGraphTooltip") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(trace, outputId);
+            ITimeGraphDataProvider<@NonNull ITimeGraphEntryModel> provider = getTimeGraphProvider(experiment, outputId);
 
             if (provider == null) {
                 // The analysis cannot be run on this trace
@@ -513,8 +514,8 @@
     /**
      * Query the provider for table columns
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -526,10 +527,10 @@
     @Path("/table/{outputId}/columns")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getColumns(@PathParam("uuid") UUID uuid,
+    public Response getColumns(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
-        Response response = getTree(uuid, outputId, queryParameters);
+        Response response = getTree(expUUID, outputId, queryParameters);
         Object entity = response.getEntity();
         if (!(entity instanceof TmfModelResponse<?>)) {
             return response;
@@ -549,8 +550,8 @@
     /**
      * Query the provider for table lines
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -562,7 +563,7 @@
     @Path("/table/{outputId}/lines")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getLines(@PathParam("uuid") UUID uuid,
+    public Response getLines(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
         if (outputId == null) {
@@ -570,12 +571,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getLines") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfVirtualTableDataProvider<? extends IVirtualTableLine, ? extends ITmfTreeDataModel> provider = manager.getDataProvider(trace, outputId, ITmfVirtualTableDataProvider.class);
+            ITmfVirtualTableDataProvider<? extends IVirtualTableLine, ? extends ITmfTreeDataModel> provider = manager.getDataProvider(experiment, outputId, ITmfVirtualTableDataProvider.class);
             if (provider == null) {
                 return Response.status(Status.METHOD_NOT_ALLOWED).entity(NO_PROVIDER).build();
             }
@@ -656,23 +657,23 @@
         return descriptors;
     }
 
-    private Response getTree(UUID uuid, String outputId, QueryParameters queryParameters) {
+    private Response getTree(UUID expUUID, String outputId, QueryParameters queryParameters) {
         if (outputId == null) {
             return Response.status(Status.PRECONDITION_FAILED).entity(MISSING_OUTPUTID).build();
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getTree") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(trace,
+            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(experiment,
                     outputId, ITmfTreeDataProvider.class);
 
             if (provider == null) {
                 // try and find the XML provider for the ID.
-                provider = getXmlProvider(trace, outputId, EnumSet.allOf(OutputType.class));
+                provider = getXmlProvider(experiment, outputId, EnumSet.allOf(OutputType.class));
             }
 
             if (provider == null) {
@@ -694,8 +695,8 @@
     /**
      * Query the provider for styles
      *
-     * @param uuid
-     *            desired trace UUID
+     * @param expUUID
+     *            desired experiment UUID
      * @param outputId
      *            Eclipse extension point ID for the data provider to query
      * @param queryParameters
@@ -707,7 +708,7 @@
     @Path("/{outputId}/style")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getStyles(@PathParam("uuid") UUID uuid,
+    public Response getStyles(@PathParam("expUUID") UUID expUUID,
             @PathParam("outputId") String outputId,
             QueryParameters queryParameters) {
         if (outputId == null) {
@@ -715,12 +716,12 @@
         }
         try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "DataProviderService#getStyles") //$NON-NLS-1$
                 .setCategory(outputId).build()) {
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            TmfExperiment experiment = ExperimentManagerService.getExperimentByUUID(expUUID);
+            if (experiment == null) {
                 return Response.status(Status.NOT_FOUND).entity(NO_SUCH_TRACE).build();
             }
 
-            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(trace,
+            ITmfTreeDataProvider<? extends @NonNull ITmfTreeDataModel> provider = manager.getDataProvider(experiment,
                     outputId, ITmfTreeDataProvider.class);
 
             if (provider == null) {
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Experiment.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Experiment.java
new file mode 100644
index 0000000..34702aa
--- /dev/null
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Experiment.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+
+import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Experiment model for TSP
+ */
+public final class Experiment implements Serializable {
+    private static final long serialVersionUID = -3626414315455912960L;
+    private final String fName;
+    private final UUID fUUID;
+    private final long fNbEvents;
+    private final long fStart;
+    private final long fEnd;
+    private final String fIndexingStatus;
+    private final Set<Trace> fTraces;
+
+    /**
+     * {@link JsonCreator} Constructor for final fields
+     *
+     * @param name
+     *            experiment name
+     * @param uuid
+     *            the stub's UUID
+     * @param nbEvents
+     *            number of current indexed events
+     * @param start
+     *            start time
+     * @param end
+     *            end time
+     * @param indexingStatus
+     *            indexing status
+     * @param traces
+     *            traces
+     */
+    @JsonCreator
+    public Experiment(@JsonProperty("name") String name,
+            @JsonProperty("UUID") UUID uuid,
+            @JsonProperty("nbEvents") long nbEvents,
+            @JsonProperty("start") long start,
+            @JsonProperty("end") long end,
+            @JsonProperty("indexingStatus") String indexingStatus,
+            @JsonProperty("traces") Set<Trace> traces) {
+        fName = name;
+        fUUID = uuid;
+        fNbEvents = nbEvents;
+        fStart = start;
+        fEnd = end;
+        fIndexingStatus = indexingStatus;
+        fTraces = traces;
+    }
+
+    /**
+     * Constructs an experiment model
+     *
+     * @param experiment
+     *            experiment
+     * @param expUUID
+     *            experiment UUID
+     * @return the experiment model
+     */
+    public static Experiment from(TmfExperiment experiment, UUID expUUID) {
+        Iterator<UUID> iter = ExperimentManagerService.getTraceUUIDs(expUUID).iterator();
+        Set<Trace> traces = Sets.newLinkedHashSet(Lists.transform(experiment.getTraces(),
+                t -> Trace.from(t, iter.next())));
+        return new Experiment(experiment.getName(),
+                expUUID,
+                experiment.getNbEvents(),
+                experiment.getStartTime().toNanos(),
+                experiment.getEndTime().toNanos(),
+                experiment.isIndexing() ? "RUNNING" : "COMPLETED",
+                traces);
+    }
+
+    /**
+     * Returns the name
+     * @return the name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Returns the UUID
+     * @return the UUID
+     */
+    public UUID getUUID() {
+        return fUUID;
+    }
+
+    /**
+     * Returns the number of events
+     * @return the number of events
+     */
+    public long getNbEvents() {
+        return fNbEvents;
+    }
+
+    /**
+     * Returns the start time
+     * @return the start time
+     */
+    public long getStart() {
+        return fStart;
+    }
+
+    /**
+     * Returns the end time
+     * @return the end time
+     */
+    public long getEnd() {
+        return fEnd;
+    }
+
+    /**
+     * Returns the indexing status
+     * @return the indexing status
+     */
+    public String getIndexingStatus() {
+        return fIndexingStatus;
+    }
+
+    /**
+     * Returns the traces
+     * @return the traces
+     */
+    public Set<Trace> getTraces() {
+        return fTraces;
+    }
+
+    @Override
+    public String toString() {
+        return "Experiment [fName=" + fName + ", fUUID=" + fUUID + ", fNbEvents=" + fNbEvents + ", fStart=" + fStart + ", fEnd=" + fEnd + ", fIndexingStatus=" + fIndexingStatus + ", fTraces=" + fTraces + "]";
+    }
+}
\ No newline at end of file
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/ExperimentManagerService.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/ExperimentManagerService.java
index d196a97..657e7e0 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/ExperimentManagerService.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/ExperimentManagerService.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -11,9 +11,15 @@
 
 package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services;
 
-import java.lang.reflect.InvocationTargetException;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
 
 import javax.ws.rs.Consumes;
@@ -27,12 +33,23 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
-import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters;
-import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
+import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
-import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
@@ -41,9 +58,9 @@
 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Iterables;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multiset;
 
 /**
  * Service to manage experiments
@@ -53,7 +70,12 @@
 @Path("/experiments")
 public class ExperimentManagerService {
 
-    private static final String EXPERIMENTS = "experiments"; //$NON-NLS-1$
+    private static final Map<UUID, TmfExperiment> EXPERIMENTS = Collections.synchronizedMap(new HashMap<>());
+    private static final Map<UUID, List<UUID>> TRACE_UUIDS = Collections.synchronizedMap(new HashMap<>());
+
+    private static final String EXPERIMENTS_FOLDER = "Experiments"; //$NON-NLS-1$
+    private static final String TRACES_FOLDER = "Traces"; //$NON-NLS-1$
+    private static final String SUFFIX = "_exp"; //$NON-NLS-1$
 
     /**
      * Getter for the list of experiments from the trace manager
@@ -63,63 +85,91 @@
     @GET
     @Produces({ MediaType.APPLICATION_JSON })
     public Response getExperiments() {
-        return Response.ok(Collections2.filter(TmfTraceManager.getInstance().getOpenedTraces(), TmfExperiment.class::isInstance)).build();
+        synchronized (EXPERIMENTS) {
+            List<Experiment> experiments = Lists.transform(new ArrayList<>(EXPERIMENTS.entrySet()),
+                    e -> Experiment.from(e.getValue(), e.getKey()));
+            return Response.ok(experiments).build();
+        }
     }
 
     /**
      * Getter for an experiment by {@link UUID}.
      *
-     * @param uuid
+     * @param expUUID
      *            UUID of the experiment to search for
      *
      * @return The experiment with the queried {@link UUID} if it exists.
      */
     @GET
-    @Path("/{uuid}")
+    @Path("/{expUUID}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public Response getExperiment(@PathParam("uuid") UUID uuid) {
-        ITmfTrace experiment = TraceManagerService.getTraceByUUID(uuid);
-        if (experiment instanceof TmfExperiment) {
-            return Response.ok(experiment).build();
-        }
+    public Response getExperiment(@PathParam("expUUID") UUID expUUID) {
+        TmfExperiment experiment = EXPERIMENTS.get(expUUID);
+        if (experiment != null) {
+            return Response.ok(Experiment.from(experiment, expUUID)).build();        }
         return Response.status(Status.NOT_FOUND).build();
     }
 
     /**
      * Get the outputs for an experiment
      *
-     * @param uuid
+     * @param expUUID
      *            UUID of the experiment to get the outputs for
      * @return The outputs for the experiment
      */
     @GET
-    @Path("/{uuid}/outputs")
+    @Path("/{expUUID}/outputs")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getOutputs(@PathParam("uuid") UUID uuid) {
-        return Response.status(Status.NOT_IMPLEMENTED).entity("Not implemented for " + uuid).build(); //$NON-NLS-1$
+    public Response getOutputs(@PathParam("expUUID") UUID expUUID) {
+        return Response.status(Status.NOT_IMPLEMENTED).entity("Not implemented for " + expUUID).build(); //$NON-NLS-1$
     }
 
 
     /**
      * Delete an experiment by {@link UUID}.
      *
-     * @param uuid
+     * @param expUUID
      *            UUID of the experiment to delete
      *
      * @return The experiment with the queried {@link UUID} if it exists.
      */
     @DELETE
-    @Path("/{uuid}")
+    @Path("/{expUUID}")
     @Produces({ MediaType.APPLICATION_JSON })
-    public Response deleteExperiment(@PathParam("uuid") UUID uuid) {
-        ITmfTrace experiment = TraceManagerService.getTraceByUUID(uuid);
-        if (experiment instanceof TmfExperiment) {
-            TmfSignalManager.dispatchSignal(new TmfTraceClosedSignal(this, experiment));
-            experiment.dispose();
-            TmfTraceManager.deleteSupplementaryFolder(experiment);
-            return Response.ok(experiment).build();
+    public Response deleteExperiment(@PathParam("expUUID") UUID expUUID) {
+        TmfExperiment experiment = EXPERIMENTS.remove(expUUID);
+        if (experiment == null) {
+            return Response.status(Status.NOT_FOUND).build();
         }
-        return Response.status(Status.NOT_FOUND).build();
+        Experiment entity = Experiment.from(experiment, expUUID);
+        TRACE_UUIDS.remove(expUUID);
+
+        TmfSignalManager.dispatchSignal(new TmfTraceClosedSignal(this, experiment));
+        experiment.dispose();
+
+        IResource resource = experiment.getResource();
+        boolean deleteResources = true;
+        synchronized (EXPERIMENTS) {
+            for (TmfExperiment e : EXPERIMENTS.values()) {
+                if (resource.equals(e.getResource())) {
+                    deleteResources = false;
+                    break;
+                }
+            }
+        }
+        if (deleteResources) {
+            try {
+                ResourcesPlugin.getWorkspace().run(mon -> {
+                    // Delete supplementary files
+                    TmfTraceManager.deleteSupplementaryFolder(experiment);
+                    // Finally, delete the experiment
+                    resource.delete(true, null);
+                }, experiment.getResource().getProject(), IWorkspace.AVOID_UPDATE, null);
+            } catch (CoreException e) {
+                return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+            }
+        }
+        return Response.ok(entity).build();
     }
 
     /**
@@ -140,77 +190,64 @@
     @Produces(MediaType.APPLICATION_JSON)
     public Response postExperiment(QueryParameters queryParameters) {
         Map<String, Object> parameters = queryParameters.getParameters();
-        String name = (String) parameters.get("name"); //$NON-NLS-1$
-        Object traces = parameters.get("traces"); //$NON-NLS-1$
-        if (! (traces instanceof List<?>)) {
+        Object nameObj = parameters.get("name"); //$NON-NLS-1$
+        Object tracesObj = parameters.get("traces"); //$NON-NLS-1$
+        if (!(nameObj instanceof String) || !(tracesObj instanceof List<?>)) {
             return Response.status(Status.BAD_REQUEST).build();
         }
-        List<?> traceUUIDs = (List<?>) traces;
-        IPath ipath = new org.eclipse.core.runtime.Path(EXPERIMENTS)
-                .append(UUID.nameUUIDFromBytes(String.join("", (Iterable) traceUUIDs).getBytes()).toString())
-                .append(name);
+        String name = (String) nameObj;
+        List<UUID> traceUUIDs = new ArrayList<>();
 
-        Optional<@NonNull ITmfTrace> optional = Iterables.tryFind(TmfTraceManager.getInstance().getOpenedTraces(), t -> t.getPath().equals(ipath.toOSString()));
-        if (optional.isPresent()) {
-            ITmfTrace trace = optional.get();
-            if (!(trace instanceof TmfExperiment)) {
-                // Something else than an experiment exists with that name,
-                // return a conflict
-                return Response.status(Status.CONFLICT).entity(optional.get()).build();
-            }
-            TmfExperiment experiment = (TmfExperiment) trace;
-            if (experiment.getChildren().size() != traceUUIDs.size()) {
-                // Not the same number of children, the current and posted
-                // experiments are different
-                return Response.status(Status.CONFLICT).entity(optional.get()).build();
-            }
-            for (Object uuidObj : traceUUIDs) {
-                if (uuidObj instanceof String) {
-                    UUID uuid = UUID.fromString((String) uuidObj);
-                    ITmfTrace childTrace = TraceManagerService.getTraceByUUID(uuid);
-                    if (childTrace == null) {
-                        return Response.noContent().build();
-                    }
-                    ITmfEventProvider child = experiment.getChild(childTrace.getName());
-                    if (child == null || !(child instanceof ITmfTrace) || !uuid.equals(((ITmfTrace) child).getUUID())) {
-                        // The requested child is not a child of the
-                        // experiment, the experiment is different
-                        return Response.status(Status.CONFLICT).entity(optional.get()).build();
-                    }
-                } else {
-                    return Response.status(Status.BAD_REQUEST).build();
-                }
-            }
-            // It's the same experiment, return OK
-            return Response.ok(experiment).build();
-
-        }
-
-        ITmfTrace[] array = new ITmfTrace[traceUUIDs.size()];
-        int i = 0;
-        for (Object uuidObj : traceUUIDs) {
-            String uuidStr;
-            if (uuidObj instanceof String) {
-                uuidStr = (String) uuidObj;
-            } else {
+        List<IResource> traceResources = new ArrayList<>();
+        for (Object uuidObj : (List<?>) tracesObj) {
+            if (!(uuidObj instanceof String)) {
                 return Response.status(Status.BAD_REQUEST).build();
             }
-            UUID uuid = UUID.fromString(uuidStr);
-            ITmfTrace trace = TraceManagerService.getTraceByUUID(uuid);
-            if (trace == null) {
+            UUID uuid = UUID.fromString((String) uuidObj);
+            IResource traceResource = TraceManagerService.getTraceResource(uuid);
+            if (traceResource == null) {
+                // The trace should have been created first
                 return Response.noContent().build();
             }
-            // traces in the experiment need to be cloned.
-            try {
-                ITmfTrace clone = trace.getClass().getDeclaredConstructor().newInstance();
-                clone.initTrace(trace.getResource(), trace.getPath(), ITmfEvent.class, trace.getName(), trace.getTraceTypeId());
-                array[i++] = clone;
-            } catch (InstantiationException | IllegalAccessException | TmfTraceException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
-                return Response.serverError().entity(e.getMessage()).build();
-            }
+            traceResources.add(traceResource);
+            traceUUIDs.add(uuid);
         }
 
-        TmfExperiment experiment = new TmfExperiment(ITmfEvent.class, ipath.toOSString(), array, TmfExperiment.DEFAULT_INDEX_PAGE_SIZE, null);
+        UUID expUUID = UUID.nameUUIDFromBytes(Objects.requireNonNull(name.getBytes(Charset.defaultCharset())));
+        IFolder resource;
+        try {
+            resource = getExperimentResource(name);
+            if (resource.exists()) {
+                // An experiment with that name has already been created
+                Multiset<IResource> oldTraceResources = HashMultiset.create(getTraceResources(resource));
+                Multiset<IResource> newTraceResources = HashMultiset.create(traceResources);
+                if (!oldTraceResources.equals(newTraceResources)) {
+                    // It's a different experiment, return a conflict
+                    TmfExperiment oldExperiment = new TmfExperiment(ITmfEvent.class, resource.getLocation().toOSString(), new ITmfTrace[0], TmfExperiment.DEFAULT_INDEX_PAGE_SIZE, resource);
+                    Experiment entity = Experiment.from(oldExperiment, expUUID);
+                    oldExperiment.dispose();
+                    return Response.status(Status.CONFLICT).entity(entity).build();
+                }
+                // It's the same experiment, check if it is opened already
+                TmfExperiment experiment = EXPERIMENTS.get(expUUID);
+                if (experiment != null) {
+                    // It's already opened, return it
+                    return Response.ok(Experiment.from(experiment, expUUID)).build();
+                }
+                // It's not opened, continue below to instantiate it
+            } else {
+                // It's a new experiment, create the experiment resources
+                createExperiment(resource, traceResources);
+            }
+            // Create and set the supplementary folder
+            createSupplementaryFolder(resource);
+        } catch (CoreException e) {
+            return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
+        }
+
+        // Instantiate the experiment and return it
+        ITmfTrace[] traces = Lists.transform(traceUUIDs, uuid -> TraceManagerService.createTraceInstance(uuid)).toArray(new ITmfTrace[0]);
+        TmfExperiment experiment = new TmfExperiment(ITmfEvent.class, resource.getLocation().toOSString(), traces, TmfExperiment.DEFAULT_INDEX_PAGE_SIZE, resource);
         experiment.indexTrace(false);
         // read first event to make sure start time is initialized
         ITmfContext ctx = experiment.seekEvent(0);
@@ -218,7 +255,129 @@
         ctx.dispose();
 
         TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, experiment, null));
-        return Response.ok(experiment).build();
+
+        TRACE_UUIDS.put(expUUID, traceUUIDs);
+        EXPERIMENTS.put(expUUID, experiment);
+
+        return Response.ok(Experiment.from(experiment, expUUID)).build();
     }
 
+    /**
+     * Gets the Eclipse resource from the experiment name.
+     *
+     * @param name
+     *            the experiment name
+     * @return The Eclipse resource
+     *
+     * @throws CoreException
+     *             if an error occurs
+     */
+    private static IFolder getExperimentResource(String name) throws CoreException {
+        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+        IProject project = root.getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME);
+        project.refreshLocal(IResource.DEPTH_INFINITE, null);
+        IFolder experimentsFolder = project.getFolder(EXPERIMENTS_FOLDER);
+        return experimentsFolder.getFolder(name);
+    }
+
+    private static List<IResource> getTraceResources(IFolder folder) {
+        final List<IResource> list = new ArrayList<>();
+        final IFolder tracesFolder = folder.getProject().getFolder(TRACES_FOLDER);
+        try {
+            folder.accept(new IResourceProxyVisitor() {
+                @Override
+                public boolean visit(IResourceProxy resource) throws CoreException {
+                    if (resource.getType() == IResource.FILE) {
+                        IResource traceResourceUnderExperiment = resource.requestResource();
+                        IPath relativePath = traceResourceUnderExperiment.getProjectRelativePath().makeRelativeTo(folder.getProjectRelativePath());
+                        IResource traceResource = tracesFolder.findMember(relativePath);
+                        if (traceResource != null) {
+                            list.add(traceResource);
+                        }
+                        return false;
+                    }
+                    return true;
+                }
+            }, IResource.NONE);
+        } catch (CoreException e) {
+        }
+        list.sort(Comparator.comparing(resource -> resource.getFullPath().toString()));
+        return list;
+    }
+
+    private static void createExperiment(IFolder folder, List<IResource> traceResources) throws CoreException {
+        // create the experiment folder resource
+        createFolder(folder);
+        // add the traces
+        for (IResource traceResource : traceResources) {
+            addTrace(folder, traceResource);
+        }
+        // set the experiment type
+        folder.setPersistentProperty(TmfCommonConstants.TRACETYPE, TmfTraceType.DEFAULT_EXPERIMENT_TYPE);
+    }
+
+    private static void addTrace(IFolder folder, IResource traceResource) throws CoreException {
+        /*
+         * Create an empty file to represent the experiment trace. The file's element
+         * path relative to the experiment resource corresponds to the trace's element
+         * path relative to the Traces folder.
+         */
+        IPath relativePath = traceResource.getProjectRelativePath().removeFirstSegments(1);
+        IFile file = folder.getFile(relativePath);
+        createFolder((IFolder) file.getParent());
+        file.create(new ByteArrayInputStream(new byte[0]), false, new NullProgressMonitor());
+        file.setPersistentProperty(TmfCommonConstants.TRACETYPE, TmfTraceType.getTraceTypeId(traceResource));
+    }
+
+    private static void createSupplementaryFolder(IFolder folder) throws CoreException {
+        IFolder supplRootFolder = folder.getProject().getFolder(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER_NAME);
+        IFolder supplFolder = supplRootFolder.getFolder(folder.getName() + SUFFIX);
+        createFolder(supplFolder);
+        folder.setPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER, supplFolder.getLocation().toOSString());
+    }
+
+    private static void createFolder(IFolder folder) throws CoreException {
+        if (!folder.exists()) {
+            if (folder.getParent() instanceof IFolder) {
+                createFolder((IFolder) folder.getParent());
+            }
+            folder.create(true, true, null);
+        }
+    }
+
+    /**
+     * Try and find an experiment with the queried UUID in the experiment
+     * manager.
+     *
+     * @param expUUID
+     *            queried {@link UUID}
+     * @return the experiment or null if none match.
+     */
+    public static @Nullable TmfExperiment getExperimentByUUID(UUID expUUID) {
+        return EXPERIMENTS.get(expUUID);
+    }
+
+    /**
+     * Get the list of trace UUIDs of an experiment from the experiment manager.
+     *
+     * @param expUUID
+     *            queried {@link UUID}
+     * @return the list of trace UUIDs.
+     */
+    public static List<UUID> getTraceUUIDs(UUID expUUID) {
+        return TRACE_UUIDS.getOrDefault(expUUID, Collections.emptyList());
+    }
+
+    /**
+     * Returns true if the given trace is in use by any experiment
+     *
+     * @param uuid
+     *            the trace UUID
+     * @return true if the given trace is in use by any experiment
+     */
+    public static boolean isTraceInUse(UUID uuid) {
+        synchronized (TRACE_UUIDS) {
+            return TRACE_UUIDS.values().stream().anyMatch(traceUUIDs -> traceUUIDs.contains(uuid));
+        }
+    }
 }
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Trace.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Trace.java
new file mode 100644
index 0000000..7067d09
--- /dev/null
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/Trace.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License 2.0 which
+ * accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services;
+
+import java.io.Serializable;
+import java.util.UUID;
+
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Trace model for TSP
+ */
+public final class Trace implements Serializable {
+    private static final long serialVersionUID = 1194829124866484394L;
+    private final String fName;
+    private final UUID fUUID;
+    private final String fPath;
+    private final long fNbEvents;
+    private final long fStart;
+    private final long fEnd;
+    private final String fIndexingStatus;
+
+    /**
+     * {@link JsonCreator} Constructor for final fields
+     *
+     * @param name
+     *            trace name
+     * @param path
+     *            path to trace on server file system
+     * @param uuid
+     *            the stub's UUID
+     * @param nbEvents
+     *            number of current indexed events
+     * @param start
+     *            start time
+     * @param end
+     *            end time
+     * @param indexingStatus
+     *            indexing status
+     */
+    @JsonCreator
+    public Trace(@JsonProperty("name") String name,
+            @JsonProperty("UUID") UUID uuid,
+            @JsonProperty("path") String path,
+            @JsonProperty("nbEvents") long nbEvents,
+            @JsonProperty("start") long start,
+            @JsonProperty("end") long end,
+            @JsonProperty("indexingStatus") String indexingStatus) {
+        fName = name;
+        fUUID = uuid;
+        fPath = path;
+        fNbEvents = nbEvents;
+        fStart = start;
+        fEnd = end;
+        fIndexingStatus = indexingStatus;
+    }
+
+    /**
+     * Constructs a trace model
+     *
+     * @param trace
+     *            trace
+     * @param uuid
+     *            UUID
+     * @return the trace model
+     */
+    public static Trace from(ITmfTrace trace, UUID uuid) {
+        return new Trace(trace.getName(),
+                uuid,
+                trace.getPath(),
+                trace.getNbEvents(),
+                trace.getStartTime().toNanos(),
+                trace.getEndTime().toNanos(),
+                trace.isIndexing() ? "RUNNING" : "COMPLETED");
+    }
+
+    /**
+     * Returns the name
+     * @return the name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Returns the UUID
+     * @return the UUID
+     */
+    public UUID getUUID() {
+        return fUUID;
+    }
+
+    /**
+     * Returns the path
+     * @return the path
+     */
+    public String getPath() {
+        return fPath;
+    }
+
+    /**
+     * Returns the number of events
+     * @return the number of events
+     */
+    public long getNbEvents() {
+        return fNbEvents;
+    }
+
+    /**
+     * Returns the start time
+     * @return the start time
+     */
+    public long getStart() {
+        return fStart;
+    }
+
+    /**
+     * Returns the end time
+     * @return the end time
+     */
+    public long getEnd() {
+        return fEnd;
+    }
+
+    /**
+     * Returns the indexing status
+     * @return the indexing status
+     */
+    public String getIndexingStatus() {
+        return fIndexingStatus;
+    }
+
+    @Override
+    public String toString() {
+        return "Trace [fName=" + fName + ", fUUID=" + fUUID + ", fPath=" + fPath + ", fNbEvents=" + fNbEvents + ", fStart=" + fStart + ", fEnd=" + fEnd + ", fIndexingStatus=" + fIndexingStatus + "]";
+    }
+}
\ No newline at end of file
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/TraceManagerService.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/TraceManagerService.java
index 2a78a85..9f3fcbb 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/TraceManagerService.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/services/TraceManagerService.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 Ericsson
+ * Copyright (c) 2017, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -12,10 +12,18 @@
 package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.Charset;
 import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.UUID;
 
 import javax.validation.constraints.NotNull;
@@ -30,7 +38,7 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
-import org.eclipse.core.resources.IFile;
+import org.apache.commons.io.FileUtils;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -50,18 +58,8 @@
 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException;
 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
 import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
-import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
-import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
-import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
-import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Iterables;
 
 /**
  * Service to manage traces.
@@ -71,6 +69,10 @@
 @Path("/traces")
 public class TraceManagerService {
 
+    private static final Map<UUID, IResource> TRACES = Collections.synchronizedMap(new HashMap<>());
+
+    private static final String TRACES_FOLDER = "Traces"; //$NON-NLS-1$
+
     /**
      * Getter method to access the list of traces
      *
@@ -79,13 +81,20 @@
     @GET
     @Produces({ MediaType.APPLICATION_JSON })
     public Response getTraces() {
-        return Response.ok(Collections2.filter(TmfTraceManager.getInstance().getOpenedTraces(),
-                Predicates.not(TmfExperiment.class::isInstance))).build();
+        synchronized (TRACES) {
+            List<Trace> traces = new ArrayList<>();
+            for (UUID uuid : TRACES.keySet()) {
+                Trace trace = createTraceModel(uuid);
+                if (trace != null) {
+                    traces.add(trace);
+                }
+            }
+            return Response.ok(traces).build();
+        }
     }
 
     /**
-     * Method to open the trace, initialize it, index it and add it to the trace
-     * manager.
+     * Method to create the trace resources and add it to the trace manager.
      *
      * @param queryParameters
      *            Parameters to post a trace as described by
@@ -102,50 +111,105 @@
         Object typeIDObject = parameters.get("typeID");
         String typeID = typeIDObject != null ? (String) typeIDObject : "";
 
-        if (!Paths.get(path).toFile().exists()) {
-            return Response.status(Status.NOT_FOUND).entity("No trace at " + path).build(); //$NON-NLS-1$
-        }
-
-        Optional<@NonNull ITmfTrace> optional = Iterables.tryFind(TmfTraceManager.getInstance().getOpenedTraces(), t -> t.getPath().equals(path));
-        if (optional.isPresent()) {
-            return Response.ok(optional.get()).build();
-        }
-
         try {
-            ITmfTrace trace = put(path, name, typeID);
-            if (trace == null) {
-                return Response.status(Status.NOT_IMPLEMENTED).entity("Trace type not supported").build(); //$NON-NLS-1$
-            }
-            return Response.ok(trace).build();
-        } catch (TmfTraceException | TmfTraceImportException | InstantiationException
-                | IllegalAccessException | CoreException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+            return put(path, name, typeID);
+        } catch (TmfTraceImportException | CoreException | IllegalArgumentException | SecurityException e) {
             return Response.status(Status.NOT_ACCEPTABLE).entity(e.getMessage()).build();
         }
     }
 
-    private ITmfTrace put(String path, String name, String typeID)
-            throws TmfTraceException, TmfTraceImportException, InstantiationException,
-            IllegalAccessException, CoreException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
-        List<TraceTypeHelper> traceTypes = TmfTraceType.selectTraceType(path, typeID);
-        if (traceTypes.isEmpty()) {
-            return null;
+    private static Response put(String path, String name, String typeID)
+            throws TmfTraceImportException, CoreException, IllegalArgumentException, SecurityException {
+
+        if (!Paths.get(path).toFile().exists()) {
+            return Response.status(Status.NOT_FOUND).entity("No trace at " + path).build(); //$NON-NLS-1$
         }
 
-        IResource resource = getResource(path);
+        List<TraceTypeHelper> traceTypes = TmfTraceType.selectTraceType(path, typeID);
+        if (traceTypes.isEmpty()) {
+            return Response.status(Status.NOT_IMPLEMENTED).entity("Trace type not supported").build(); //$NON-NLS-1$
+        }
+        String traceType = traceTypes.get(0).getTraceTypeId();
 
-//        TmfTraceTypeUIUtils.setTraceType(resource, traceTypeHelper);
-        TraceTypeHelper helper = traceTypes.get(0);
-        resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, helper.getTraceTypeId());
+        UUID uuid = UUID.nameUUIDFromBytes(Objects.requireNonNull((path + name).getBytes(Charset.defaultCharset())));
 
-        ITmfTrace trace = helper.getTraceClass().getDeclaredConstructor().newInstance();
-        trace.initTrace(resource, path, ITmfEvent.class, name, typeID);
-        trace.indexTrace(false);
-        // read first event to make sure start time is initialized
-        ITmfContext ctx = trace.seekEvent(0);
-        trace.getNext(ctx);
-        ctx.dispose();
+        IResource resource = getResource(path, name);
+        if (!resource.exists()) {
+            if (!createResource(path, resource)) {
+                return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Trace resource creation failed").build(); //$NON-NLS-1$
+            }
+            resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceType);
+        } else {
+            IPath targetLocation = org.eclipse.core.runtime.Path.forPosix(path).removeTrailingSeparator();
+            IPath oldLocation = ResourceUtil.getLocation(resource);
+            if (oldLocation == null || !targetLocation.equals(oldLocation.removeTrailingSeparator()) ||
+                    !traceType.equals(resource.getPersistentProperty(TmfCommonConstants.TRACETYPE))) {
+                synchronized (TRACES) {
+                    Optional<@NonNull Entry<UUID, IResource>> oldEntry = TRACES.entrySet().stream().filter(entry -> resource.equals(entry.getValue())).findFirst();
+                    if (!oldEntry.isPresent()) {
+                        return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Failed to find conflicting trace").build(); //$NON-NLS-1$
+                    }
+                    UUID oldUUID = oldEntry.get().getKey();
+                    return Response.status(Status.CONFLICT).entity(createTraceModel(oldUUID)).build();
+                }
+            }
+        }
+        TRACES.put(uuid, resource);
+        return Response.ok(createTraceModel(uuid)).build();
+    }
 
-        TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, trace, null));
+    /**
+     * Get the resource of a trace by its UUID.
+     * @param uuid
+     *            the trace UUID
+     * @return the trace resource, or null if it could not be found
+     */
+    public static @Nullable IResource getTraceResource(UUID uuid) {
+        return TRACES.get(uuid);
+    }
+
+    /**
+     * Create an instance of a trace by its UUID. The caller is responsible to
+     * dispose the instance when it is no longer needed.
+     *
+     * @param uuid
+     *            the trace UUID
+     * @return the trace instance, or null if it could not be created
+     */
+    public static @Nullable ITmfTrace createTraceInstance(UUID uuid) {
+        try {
+            IResource resource = TRACES.get(uuid);
+            if (resource == null) {
+                return null;
+            }
+            String typeID = TmfTraceType.getTraceTypeId(resource);
+            if (typeID == null) {
+                return null;
+            }
+            TraceTypeHelper helper = TmfTraceType.getTraceType(typeID);
+            ITmfTrace trace = helper.getTraceClass().getDeclaredConstructor().newInstance();
+            String path = Objects.requireNonNull(ResourceUtil.getLocation(resource)).removeTrailingSeparator().toOSString();
+            String name = resource.getName();
+            trace.initTrace(resource, path, ITmfEvent.class, name, typeID);
+            trace.indexTrace(false);
+            // read first event to make sure start time is initialized
+            ITmfContext ctx = trace.seekEvent(0);
+            trace.getNext(ctx);
+            ctx.dispose();
+            return trace;
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | CoreException | TmfTraceException e) {
+            Activator.getInstance().logError("Failed to create trace instance for " + uuid, e); //$NON-NLS-1$
+            return null;
+        }
+    }
+
+    private static Trace createTraceModel(UUID uuid) {
+        ITmfTrace traceInstance = createTraceInstance(uuid);
+        if (traceInstance == null) {
+            return null;
+        }
+        Trace trace = Trace.from(traceInstance, uuid);
+        traceInstance.dispose();
         return trace;
     }
 
@@ -155,43 +219,59 @@
      *
      * @param path
      *            the absolute path string to the trace
+     * @param name
+     *            the trace name
      * @return The Eclipse resources
      *
      * @throws CoreException
      *             if an error occurs
      */
-    private static IResource getResource(String path) throws CoreException {
+    private static IResource getResource(String path, String name) throws CoreException {
         IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
         IProject project = root.getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME);
-        IFolder tracesFolder = project.getFolder("Traces");
-        IPath iPath = org.eclipse.core.runtime.Path.forPosix(path);
+        project.refreshLocal(IResource.DEPTH_INFINITE, null);
+        IFolder tracesFolder = project.getFolder(TRACES_FOLDER);
+        IPath targetLocation = org.eclipse.core.runtime.Path.forPosix(path);
+        IPath resourcePath = targetLocation.removeLastSegments(1).append(name);
 
         IResource resource = null;
-        boolean isSuccess = false;
         // create the resource hierarchy.
         if (new File(path).isFile()) {
-            IFile file = tracesFolder.getFile(path);
-            createFolder((IFolder) file.getParent(), null);
-            isSuccess = ResourceUtil.createSymbolicLink(file, iPath, true, null);
-            resource = file;
+            resource = tracesFolder.getFile(resourcePath);
         } else {
-            IFolder folder = tracesFolder.getFolder(path);
-            createFolder((IFolder) folder.getParent(), null);
-            isSuccess = ResourceUtil.createSymbolicLink(folder, iPath, true, null);
-            resource = folder;
+            resource = tracesFolder.getFolder(resourcePath);
         }
+        return resource;
+    }
 
-        if (!isSuccess) {
-            return null;
+    /**
+     * Create the Eclipse resource from the target location and prepare the
+     * supplementary directory for this trace.
+     *
+     * @param path
+     *            the absolute path string to the trace
+     * @param name
+     *            the trace name
+     * @return true if creation was successful
+     *
+     * @throws CoreException
+     *             if an error occurs
+     */
+    private static boolean createResource(String path, IResource resource) throws CoreException {
+        // create the resource hierarchy.
+        IPath targetLocation = org.eclipse.core.runtime.Path.forPosix(path);
+        createFolder((IFolder) resource.getParent(), null);
+        if (!ResourceUtil.createSymbolicLink(resource, targetLocation, true, null)) {
+            return false;
         }
 
         // create supplementary folder on file system:
-        IFolder supplRootFolder = project.getFolder(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER_NAME);
-        IFolder supplFolder = supplRootFolder.getFolder(path);
+        IFolder supplRootFolder = resource.getProject().getFolder(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER_NAME);
+        IFolder supplFolder = supplRootFolder.getFolder(resource.getProjectRelativePath().removeFirstSegments(1));
         createFolder(supplFolder, null);
         resource.setPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER, supplFolder.getLocation().toOSString());
 
-        return resource;
+        return true;
     }
 
     /**
@@ -205,15 +285,15 @@
     @Path("/{uuid}")
     @Produces({ MediaType.APPLICATION_JSON })
     public Response getTrace(@PathParam("uuid") @NotNull UUID uuid) {
-        ITmfTrace trace = getTraceByUUID(uuid);
-        if (trace == null || trace instanceof TmfExperiment) {
+        Trace trace = createTraceModel(uuid);
+        if (trace == null) {
             return Response.status(Status.NOT_FOUND).build();
         }
         return Response.ok(trace).build();
     }
 
     /**
-     * Delete a trace from the manager and dispose of it
+     * Delete a trace from the manager
      *
      * @param uuid
      *            Unique trace ID
@@ -223,38 +303,37 @@
     @Path("/{uuid}")
     @Produces({ MediaType.APPLICATION_JSON })
     public Response deleteTrace(@PathParam("uuid") @NotNull UUID uuid) {
-        ITmfTrace trace = getTraceByUUID(uuid);
-        if (trace == null || trace instanceof TmfExperiment) {
+        Trace trace = createTraceModel(uuid);
+        if (trace == null) {
             return Response.status(Status.NOT_FOUND).build();
         }
-        TmfSignalManager.dispatchSignal(new TmfTraceClosedSignal(this, trace));
-        trace.dispose();
-        TmfTraceManager.deleteSupplementaryFolder(trace);
+        if (ExperimentManagerService.isTraceInUse(uuid)) {
+            return Response.status(Status.CONFLICT).entity(trace).build();
+        }
+        IResource resource = TRACES.remove(uuid);
+        if (resource == null) {
+            return Response.ok(trace).build();
+        }
         try {
-            IResource resource = trace.getResource();
-            if (resource != null) {
-                resource.delete(IResource.FORCE, null);
-            }
+            // Delete supplementary files and folders
+            File supplFolder = new File(resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER));
+            FileUtils.cleanDirectory(supplFolder);
+            cleanupFolders(supplFolder,
+                    resource.getProject().getFolder(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER_NAME).getLocation().toFile());
+            // Delete trace resource
+            resource.delete(IResource.FORCE, null);
+            cleanupFolders(resource.getParent().getLocation().toFile(),
+                    resource.getProject().getFolder(TRACES_FOLDER).getLocation().toFile());
+            // Refresh the workspace
             ResourcesPlugin.getWorkspace().getRoot()
-                .getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME)
-                .refreshLocal(Integer.MAX_VALUE, null);
-        } catch (CoreException e) {
+            .getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME)
+            .refreshLocal(Integer.MAX_VALUE, null);
+        } catch (CoreException | IOException e) {
             Activator.getInstance().logError("Failed to delete trace", e); //$NON-NLS-1$
         }
         return Response.ok(trace).build();
     }
 
-    /**
-     * Try and find a trace with the queried UUID in the {@link TmfTraceManager}.
-     *
-     * @param uuid
-     *            queried {@link UUID}
-     * @return the trace or null if none match.
-     */
-    public static @Nullable ITmfTrace getTraceByUUID(UUID uuid) {
-        return Iterables.tryFind(TmfTraceManager.getInstance().getOpenedTraces(), t -> uuid.equals(t.getUUID())).orNull();
-    }
-
     private static void createFolder(IFolder folder, IProgressMonitor monitor) throws CoreException {
         // Taken from: org.eclipse.tracecompass.tmf.ui.project.model.TraceUtil.java
         // TODO: have a tmf.core util for that.
@@ -265,4 +344,12 @@
             folder.create(true, true, monitor);
         }
     }
+
+    private static void cleanupFolders(File folder, File root) {
+        File current = folder;
+        while (current.isDirectory() && !current.equals(root) && current.listFiles().length == 0) {
+            current.delete();
+            current = current.getParentFile();
+        }
+    }
 }
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/ExperimentSerializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/ExperimentSerializer.java
index 769222b..7bad9c5 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/ExperimentSerializer.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/ExperimentSerializer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -12,9 +12,9 @@
 package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp;
 
 import java.io.IOException;
-import java.util.Objects;
 
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.Experiment;
 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
 
 import com.fasterxml.jackson.core.JsonGenerator;
@@ -26,7 +26,7 @@
  *
  * @author Loic Prieur-Drevon
  */
-public class ExperimentSerializer extends StdSerializer<@NonNull TmfExperiment> {
+public class ExperimentSerializer extends StdSerializer<@NonNull Experiment> {
 
     /**
      * Generated serialVersionUID
@@ -37,19 +37,18 @@
      * Public constructor
      */
     public ExperimentSerializer() {
-        super(TmfExperiment.class);
+        super(Experiment.class);
     }
 
     @Override
-    public void serialize(TmfExperiment value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+    public void serialize(Experiment value, JsonGenerator gen, SerializerProvider provider) throws IOException {
         gen.writeStartObject();
         gen.writeStringField("name", value.getName()); //$NON-NLS-1$
-        gen.writeStringField("UUID", Objects.requireNonNull(value.getUUID()).toString()); //$NON-NLS-1$
+        gen.writeStringField("UUID", value.getUUID().toString()); //$NON-NLS-1$
         gen.writeNumberField("nbEvents", value.getNbEvents()); //$NON-NLS-1$
-        gen.writeNumberField("start", value.getStartTime().toNanos()); //$NON-NLS-1$
-        gen.writeNumberField("end", value.getEndTime().toNanos()); //$NON-NLS-1$
-        String indexingStatus = value.isIndexing() ? "RUNNING" : "COMPLETED"; //$NON-NLS-1$ //$NON-NLS-2$
-        gen.writeStringField("indexingStatus", indexingStatus); //$NON-NLS-1$
+        gen.writeNumberField("start", value.getStart()); //$NON-NLS-1$
+        gen.writeNumberField("end", value.getEnd()); //$NON-NLS-1$
+        gen.writeStringField("indexingStatus", value.getIndexingStatus()); //$NON-NLS-1$
         gen.writeObjectField("traces", value.getTraces()); //$NON-NLS-1$
         gen.writeEndObject();
     }
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/TraceSerializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/TraceSerializer.java
index 1f92c0d..39b418b 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/TraceSerializer.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/TraceSerializer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Ericsson
+ * Copyright (c) 2018, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -12,22 +12,20 @@
 package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp;
 
 import java.io.IOException;
-import java.util.Objects;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.Trace;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
 
 /**
- * {@link StdSerializer} for {@link ITmfTrace} to avoid building intermediate
- * representations.
+ * {@link StdSerializer} for {@link Trace}.
  *
  * @author Loic Prieur-Drevon
  */
-public class TraceSerializer extends StdSerializer<@NonNull ITmfTrace> {
+public class TraceSerializer extends StdSerializer<@NonNull Trace> {
 
     /**
      * Generated serialVersionUID
@@ -38,21 +36,19 @@
      * Public constructor
      */
     public TraceSerializer() {
-        super(ITmfTrace.class);
+        super(Trace.class);
     }
 
     @Override
-    public void serialize(ITmfTrace value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+    public void serialize(Trace value, JsonGenerator gen, SerializerProvider provider) throws IOException {
         gen.writeStartObject();
         gen.writeStringField("name", value.getName()); //$NON-NLS-1$
         gen.writeStringField("path", value.getPath()); //$NON-NLS-1$
-        gen.writeStringField("UUID", Objects.requireNonNull(value.getUUID()).toString()); //$NON-NLS-1$
+        gen.writeStringField("UUID", value.getUUID().toString()); //$NON-NLS-1$
         gen.writeNumberField("nbEvents", value.getNbEvents()); //$NON-NLS-1$
-        gen.writeNumberField("start", value.getStartTime().toNanos()); //$NON-NLS-1$
-        gen.writeNumberField("end", value.getEndTime().toNanos()); //$NON-NLS-1$
-        // TODO Find a better way, no support for cancel
-        String indexingStatus = value.isIndexing() ? "RUNNING" : "COMPLETED"; //$NON-NLS-1$ //$NON-NLS-2$
-        gen.writeStringField("indexingStatus", indexingStatus); //$NON-NLS-1$
+        gen.writeNumberField("start", value.getStart()); //$NON-NLS-1$
+        gen.writeNumberField("end", value.getEnd()); //$NON-NLS-1$
+        gen.writeStringField("indexingStatus", value.getIndexingStatus()); //$NON-NLS-1$
         gen.writeEndObject();
     }
 
diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/WebApplication.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/WebApplication.java
index 1ae0ef3..2ba5ec6 100644
--- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/WebApplication.java
+++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/WebApplication.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 Ericsson
+ * Copyright (c) 2017, 2020 Ericsson
  *
  * All rights reserved. This program and the accompanying materials are
  * made available under the terms of the Eclipse Public License 2.0 which
@@ -24,8 +24,10 @@
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.DataProviderService;
+import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.Experiment;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.ExperimentManagerService;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.FilterService;
+import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.Trace;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.TraceManagerService;
 import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.XmlManagerService;
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.table.IVirtualTableLine;
@@ -39,8 +41,6 @@
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
 import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel;
 import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.servlet.ServletContainer;
 
@@ -173,8 +173,8 @@
         provider.setMapper(mapper);
 
         SimpleModule module = new SimpleModule();
-        module.addSerializer(ITmfTrace.class, new TraceSerializer());
-        module.addSerializer(TmfExperiment.class, new ExperimentSerializer());
+        module.addSerializer(Trace.class, new TraceSerializer());
+        module.addSerializer(Experiment.class, new ExperimentSerializer());
         module.addSerializer(DataProviderDescriptor.class, new DataProviderDescriptorSerializer());
         module.addSerializer(ITmfXyModel.class, new XYModelSerializer());
         module.addSerializer(ISeriesModel.class, new SeriesModelSerializer());