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/BookmarkManagerServiceTest.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/BookmarkManagerServiceTest.java index 2b5c5a2f..dd81aead 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/BookmarkManagerServiceTest.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/BookmarkManagerServiceTest.java @@ -33,7 +33,6 @@ * Test class for BookmarkManagerService * * @author Kaveh Shahedi - * @since 10.1 */ public class BookmarkManagerServiceTest extends RestServerTest { @@ -43,6 +42,9 @@ public class BookmarkManagerServiceTest extends RestServerTest { private static final @NonNull BookmarkModelStub BOOKMARK = new BookmarkModelStub(BOOKMARK_NAME, START_TIME, END_TIME); private ExperimentModelStub experiment; + private static final String START = "start"; + private static final String END = "end"; + /** * Setup method to run before each test. Creates a clean experiment and removes all * existing bookmarks. @@ -90,24 +92,24 @@ public void testCreateBookmarkInvalidParams() { // Test with null name Map parameters = new HashMap<>(); parameters.put(NAME, null); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Should return 400 for null name", 400, response.getStatus()); // Test with non-numeric start and end times parameters.put(NAME, BOOKMARK_NAME); - parameters.put("start", "not a number"); - parameters.put("end", "not a number"); + parameters.put(START, "not a number"); + parameters.put(END, "not a number"); response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Should return 400 for non-numeric times", 400, response.getStatus()); // Test with end time before start time parameters.put(NAME, BOOKMARK_NAME); - parameters.put("start", END_TIME); - parameters.put("end", START_TIME); + parameters.put(START, END_TIME); + parameters.put(END, START_TIME); response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Should return 400 for invalid time range", 400, response.getStatus()); @@ -125,8 +127,8 @@ public void testCreateBookmark() { Map parameters = new HashMap<>(); parameters.put(NAME, BOOKMARK.getName()); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Response status should be 200", 200, response.getStatus()); @@ -152,8 +154,8 @@ public void testCreateBookmarkRepetitiveName() { // Create first bookmark Map parameters = new HashMap<>(); parameters.put(NAME, BOOKMARK.getName()); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); BookmarkModelStub firstBookmark = response.readEntity(BookmarkModelStub.class); @@ -161,8 +163,8 @@ public void testCreateBookmarkRepetitiveName() { assertNotNull("First bookmark should not be null", firstBookmark); // Try to create second bookmark with same name but different times - parameters.replace("start", START_TIME + 1); - parameters.replace("end", END_TIME + 1); + parameters.replace(START, START_TIME + 1); + parameters.replace(END, END_TIME + 1); response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Should return conflict for duplicate name", 409, response.getStatus()); @@ -190,8 +192,8 @@ public void testGetAllBookmarks() { // Create multiple bookmarks Map parameters = new HashMap<>(); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); // Create first bookmark parameters.put(NAME, "Bookmark1"); @@ -232,8 +234,8 @@ public void testGetSpecificBookmark() { // Create a bookmark Map parameters = new HashMap<>(); parameters.put(NAME, BOOKMARK.getName()); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); assertEquals("Bookmark creation should succeed", 200, response.getStatus()); @@ -264,8 +266,8 @@ public void testUpdateBookmark() { // Create initial bookmark Map parameters = new HashMap<>(); parameters.put(NAME, BOOKMARK.getName()); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); BookmarkModelStub originalBookmark = response.readEntity(BookmarkModelStub.class); @@ -278,8 +280,8 @@ public void testUpdateBookmark() { assertEquals("Should return 404 for non-existent bookmark", 404, nonExistentResponse.getStatus()); // Test updating with invalid parameters - parameters.put("start", END_TIME); - parameters.put("end", START_TIME); + parameters.put(START, END_TIME); + parameters.put(END, START_TIME); Response invalidResponse = bookmarkTarget.path(originalBookmark.getUUID().toString()) .request() .put(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); @@ -287,8 +289,8 @@ public void testUpdateBookmark() { // Test successful update parameters.put("name", "Updated Name"); - parameters.put("start", START_TIME + 5); - parameters.put("end", END_TIME + 5); + parameters.put(START, START_TIME + 5); + parameters.put(END, END_TIME + 5); response = bookmarkTarget.path(originalBookmark.getUUID().toString()) .request() @@ -323,8 +325,8 @@ public void testDeleteBookmark() { // Create a bookmark to delete Map parameters = new HashMap<>(); parameters.put(NAME, BOOKMARK.getName()); - parameters.put("start", START_TIME); - parameters.put("end", END_TIME); + parameters.put(START, START_TIME); + parameters.put(END, END_TIME); Response response = bookmarkTarget.request().post(Entity.json(new QueryParameters(parameters, Collections.emptyList()))); BookmarkModelStub createdBookmark = response.readEntity(BookmarkModelStub.class); 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/BookmarkModelStub.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/BookmarkModelStub.java index e3f8e3c5..9c510f11 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/BookmarkModelStub.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/BookmarkModelStub.java @@ -25,7 +25,6 @@ * BookmarkModel schema * * @author Kaveh Shahedi - * @since 10.1 */ @JsonIgnoreProperties(ignoreUnknown = true) public class BookmarkModelStub implements Serializable { 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/model/Bookmark.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Bookmark.java index 61e66cbd..b85b33ba 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Bookmark.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Bookmark.java @@ -23,7 +23,6 @@ * Contributes to the model used for TSP swagger-core annotations. * * @author Kaveh Shahedi - * @since 10.1 */ public interface Bookmark { 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/model/BookmarkQueryParameters.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/BookmarkQueryParameters.java index 05e2e83a..40a881e7 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/BookmarkQueryParameters.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/BookmarkQueryParameters.java @@ -19,7 +19,6 @@ * Parameters for bookmark creation and update operations * * @author Kaveh Shahedi - * @since 10.1 */ public interface BookmarkQueryParameters { 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/Bookmark.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/Bookmark.java index d787a3ac..83a451fb 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/Bookmark.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/Bookmark.java @@ -19,7 +19,6 @@ * Bookmark model for TSP * * @author Kaveh Shahedi - * @since 10.1 */ public class Bookmark implements Serializable { 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/BookmarkManagerService.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/BookmarkManagerService.java index 8d3dbc72..d8b9f401 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/BookmarkManagerService.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/BookmarkManagerService.java @@ -17,15 +17,9 @@ import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.NO_SUCH_EXPERIMENT; import static org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.services.EndpointConstants.BKM; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -44,22 +38,15 @@ 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.IMarker; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceVisitor; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.Activator; import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.BookmarkQueryParameters; import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.views.QueryParameters; -import org.eclipse.tracecompass.tmf.core.TmfCommonConstants; +import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; -import com.google.common.collect.Lists; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -73,60 +60,16 @@ * Service to manage bookmarks for experiments * * @author Kaveh Shahedi - * @since 10.1 */ @Path("/experiments/{expUUID}/bookmarks") @Tag(name = BKM) public class BookmarkManagerService { - private static final Map> EXPERIMENT_BOOKMARKS = Collections.synchronizedMap(initBookmarkResources()); - private static final String BOOKMARKS_FOLDER = "Bookmarks"; //$NON-NLS-1$ - - private static Map> initBookmarkResources() { - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IProject project = root.getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME); - Map> experimentBookmarks = new HashMap<>(); - try { - project.refreshLocal(IResource.DEPTH_INFINITE, null); - IFolder bookmarksFolder = project.getFolder(BOOKMARKS_FOLDER); - // Check if the folder exists. If not, create it - if (!bookmarksFolder.exists()) { - bookmarksFolder.create(true, true, null); - } - bookmarksFolder.accept((IResourceVisitor) resource -> { - if (resource.equals(bookmarksFolder)) { - return true; - } - if (resource instanceof IFolder) { - UUID expUUID = UUID.fromString(Objects.requireNonNull(resource.getName())); - Map bookmarks = loadBookmarks((IFolder) resource); - if (!bookmarks.isEmpty()) { - experimentBookmarks.put(expUUID, bookmarks); - } - } - return false; - }, IResource.DEPTH_ONE, IResource.NONE); - } catch (CoreException e) { - Activator.getInstance().logError("Failed to load bookmarks", e); //$NON-NLS-1$ - } - return experimentBookmarks; - } - - private static Map loadBookmarks(IFolder experimentFolder) throws CoreException { - Map bookmarks = new HashMap<>(); - experimentFolder.accept(resource -> { - if (resource instanceof IFile && resource.getName().endsWith(".bookmark")) { //$NON-NLS-1$ - try (ObjectInputStream ois = new ObjectInputStream(((IFile) resource).getContents(true))) { - Bookmark bookmark = (Bookmark) ois.readObject(); - bookmarks.put(bookmark.getUUID(), bookmark); - } catch (Exception e) { - Activator.getInstance().logError("Failed to load bookmark", e); //$NON-NLS-1$ - } - } - return true; - }); - return bookmarks; - } + // Bookmark attribute constants + private static final String BOOKMARK_UUID = "uuid"; //$NON-NLS-1$ + private static final String BOOKMARK_NAME = "name"; //$NON-NLS-1$ + private static final String BOOKMARK_START = "start"; //$NON-NLS-1$ + private static final String BOOKMARK_END = "end"; //$NON-NLS-1$ /** * Retrieve all bookmarks for a specific experiment @@ -138,7 +81,7 @@ private static Map loadBookmarks(IFolder experimentFolder) throw @GET @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get all bookmarks for an experiment", responses = { - @ApiResponse(responseCode = "200", description = "Returns the list of bookmarks", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Bookmark.class)))), + @ApiResponse(responseCode = "200", description = "Returns the list of bookmarks", content = @Content(array = @ArraySchema(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Bookmark.class)))), @ApiResponse(responseCode = "404", description = NO_SUCH_EXPERIMENT, content = @Content(schema = @Schema(implementation = String.class))) }) public Response getBookmarks(@Parameter(description = EXP_UUID) @PathParam("expUUID") UUID expUUID) { @@ -147,9 +90,18 @@ public Response getBookmarks(@Parameter(description = EXP_UUID) @PathParam("expU return Response.status(Status.NOT_FOUND).entity(NO_SUCH_EXPERIMENT).build(); } - synchronized (EXPERIMENT_BOOKMARKS) { - List bookmarks = Lists.transform(new ArrayList<>(EXPERIMENT_BOOKMARKS.getOrDefault(expUUID, Collections.emptyMap()).values()), bookmark -> bookmark); + IFile editorFile = TmfTraceManager.getInstance().getTraceEditorFile(experiment); + if (editorFile == null) { + return Response.ok(Collections.emptyList()).build(); + } + + try { + IMarker[] markers = findBookmarkMarkers(editorFile); + List bookmarks = markersToBookmarks(markers); return Response.ok(bookmarks).build(); + } catch (CoreException e) { + Activator.getInstance().logError("Failed to get bookmarks", e); //$NON-NLS-1$ + return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -166,7 +118,7 @@ public Response getBookmarks(@Parameter(description = EXP_UUID) @PathParam("expU @Path("/{bookmarkUUID}") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Get a specific bookmark from an experiment", responses = { - @ApiResponse(responseCode = "200", description = "Returns the bookmark", content = @Content(schema = @Schema(implementation = Bookmark.class))), + @ApiResponse(responseCode = "200", description = "Returns the bookmark", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Bookmark.class))), @ApiResponse(responseCode = "404", description = "Experiment or bookmark not found", content = @Content(schema = @Schema(implementation = String.class))) }) public Response getBookmark( @@ -178,12 +130,22 @@ public Response getBookmark( return Response.status(Status.NOT_FOUND).entity(NO_SUCH_EXPERIMENT).build(); } - Map bookmarks = EXPERIMENT_BOOKMARKS.get(expUUID); - if (bookmarks == null || !bookmarks.containsKey(bookmarkUUID)) { + IFile editorFile = TmfTraceManager.getInstance().getTraceEditorFile(experiment); + if (editorFile == null) { return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); } - return Response.ok(bookmarks.get(bookmarkUUID)).build(); + try { + IMarker[] markers = findBookmarkMarkers(editorFile); + IMarker marker = findMarkerByUUID(markers, bookmarkUUID); + if (marker == null) { + return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); + } + return Response.ok(markerToBookmark(marker)).build(); + } catch (CoreException e) { + Activator.getInstance().logError("Failed to get bookmark", e); //$NON-NLS-1$ + return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + } } /** @@ -199,7 +161,7 @@ public Response getBookmark( @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Create a new bookmark in an experiment", responses = { - @ApiResponse(responseCode = "200", description = "Bookmark created successfully", content = @Content(schema = @Schema(implementation = Bookmark.class))), + @ApiResponse(responseCode = "200", description = "Bookmark created successfully", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Bookmark.class))), @ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))), @ApiResponse(responseCode = "404", description = NO_SUCH_EXPERIMENT, content = @Content(schema = @Schema(implementation = String.class))) }) @@ -224,53 +186,26 @@ public Response createBookmark( return Response.status(Status.NOT_FOUND).entity(NO_SUCH_EXPERIMENT).build(); } - String name = Objects.requireNonNull((String) parameters.get("name")); //$NON-NLS-1$ - long start = Objects.requireNonNull((Number) parameters.get("start")).longValue(); //$NON-NLS-1$ - long end = Objects.requireNonNull((Number) parameters.get("end")).longValue(); //$NON-NLS-1$ - - try { - IFolder bookmarkFolder = getBookmarkFolder(expUUID); - UUID bookmarkUUID = UUID.nameUUIDFromBytes(Objects.requireNonNull(name.getBytes(Charset.defaultCharset()))); - - // Check if bookmark already exists - Map existingBookmarks = EXPERIMENT_BOOKMARKS.get(expUUID); - if (existingBookmarks != null && existingBookmarks.containsKey(bookmarkUUID)) { - Bookmark existingBookmark = Objects.requireNonNull(existingBookmarks.get(bookmarkUUID)); - // Check if it's the same bookmark (same start and end times) - if (existingBookmark.getStart() != start || existingBookmark.getEnd() != end) { - // It's a different bookmark with the same name, return conflict - return Response.status(Status.CONFLICT).entity(existingBookmark).build(); - } - // It's the same bookmark, return it - return Response.ok(existingBookmark).build(); - } - - createFolder(bookmarkFolder); + String name = Objects.requireNonNull((String) parameters.get(BOOKMARK_NAME)); // $NON-NLS-1$ + long start = Objects.requireNonNull((Number) parameters.get(BOOKMARK_START)).longValue(); // $NON-NLS-1$ + long end = Objects.requireNonNull((Number) parameters.get(BOOKMARK_END)).longValue(); // $NON-NLS-1$ + UUID bookmarkUUID = UUID.nameUUIDFromBytes(Objects.requireNonNull(name.getBytes(Charset.defaultCharset()))); - Bookmark bookmark = new Bookmark(bookmarkUUID, name, start, end); + Bookmark bookmark = new Bookmark(bookmarkUUID, name, start, end); - // Save to file system - IFile bookmarkFile = bookmarkFolder.getFile(bookmarkUUID.toString() + ".bookmark"); //$NON-NLS-1$ - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos)) { - - oos.writeObject(bookmark); - oos.flush(); + IFile editorFile = TmfTraceManager.getInstance().getTraceEditorFile(experiment); + if (editorFile == null) { + return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); + } - if (bookmarkFile.exists()) { - bookmarkFile.setContents(new ByteArrayInputStream(baos.toByteArray()), IResource.FORCE, null); - } else { - bookmarkFile.create(new ByteArrayInputStream(baos.toByteArray()), true, null); - } - } catch (IOException e) { - Activator.getInstance().logError("Failed to create bookmark", e); //$NON-NLS-1$ - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + try { + IMarker[] markers = findBookmarkMarkers(editorFile); + if (bookmarkExists(markers, bookmark.getUUID())) { + return Response.status(Status.CONFLICT).entity(bookmark).build(); } - // Add to memory - Map bookmarks = EXPERIMENT_BOOKMARKS.computeIfAbsent(expUUID, k -> new HashMap<>()); - bookmarks.put(bookmarkUUID, bookmark); - + IMarker newMarker = editorFile.createMarker(IMarker.BOOKMARK); + setMarkerAttributes(newMarker, bookmark); return Response.ok(bookmark).build(); } catch (CoreException e) { Activator.getInstance().logError("Failed to create bookmark", e); //$NON-NLS-1$ @@ -294,7 +229,7 @@ public Response createBookmark( @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Update an existing bookmark in an experiment", responses = { - @ApiResponse(responseCode = "200", description = "Bookmark updated successfully", content = @Content(schema = @Schema(implementation = Bookmark.class))), + @ApiResponse(responseCode = "200", description = "Bookmark updated successfully", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Bookmark.class))), @ApiResponse(responseCode = "400", description = INVALID_PARAMETERS, content = @Content(schema = @Schema(implementation = String.class))), @ApiResponse(responseCode = "404", description = "Experiment or bookmark not found", content = @Content(schema = @Schema(implementation = String.class))) }) @@ -320,42 +255,29 @@ public Response updateBookmark( return Response.status(Status.NOT_FOUND).entity(NO_SUCH_EXPERIMENT).build(); } - Map bookmarks = EXPERIMENT_BOOKMARKS.get(expUUID); - if (bookmarks == null || !bookmarks.containsKey(bookmarkUUID)) { + String name = Objects.requireNonNull((String) parameters.get(BOOKMARK_NAME)); + long start = Objects.requireNonNull((Number) parameters.get(BOOKMARK_START)).longValue(); + long end = Objects.requireNonNull((Number) parameters.get(BOOKMARK_END)).longValue(); + + Bookmark bookmark = new Bookmark(bookmarkUUID, name, start, end); + + IFile editorFile = TmfTraceManager.getInstance().getTraceEditorFile(experiment); + if (editorFile == null) { return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); } - String name = Objects.requireNonNull((String) parameters.get("name")); //$NON-NLS-1$ - long start = Objects.requireNonNull((Number) parameters.get("start")).longValue(); //$NON-NLS-1$ - long end = Objects.requireNonNull((Number) parameters.get("end")).longValue(); //$NON-NLS-1$ - try { - IFolder bookmarkFolder = getBookmarkFolder(expUUID); - Bookmark updatedBookmark = new Bookmark(bookmarkUUID, name, start, end); - - // Update file system - IFile bookmarkFile = bookmarkFolder.getFile(bookmarkUUID.toString() + ".bookmark"); //$NON-NLS-1$ - if (bookmarkFile.exists()) { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos)) { - - oos.writeObject(updatedBookmark); - oos.flush(); - - bookmarkFile.setContents(new ByteArrayInputStream(baos.toByteArray()), IResource.FORCE, null); - // Update memory - bookmarks.put(bookmarkUUID, updatedBookmark); - return Response.ok(updatedBookmark).build(); - } + IMarker[] markers = findBookmarkMarkers(editorFile); + IMarker marker = findMarkerByUUID(markers, bookmarkUUID); + if (marker == null) { + return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); } - return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); + setMarkerAttributes(marker, bookmark); + return Response.ok(bookmark).build(); } catch (CoreException e) { Activator.getInstance().logError("Failed to update bookmark", e); //$NON-NLS-1$ return Response.status(Status.INTERNAL_SERVER_ERROR).build(); - } catch (IOException e) { - Activator.getInstance().logError("Failed to update bookmark", e); //$NON-NLS-1$ - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @@ -372,7 +294,7 @@ public Response updateBookmark( @Path("/{bookmarkUUID}") @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Delete a bookmark from an experiment", responses = { - @ApiResponse(responseCode = "200", description = "Bookmark deleted successfully", content = @Content(schema = @Schema(implementation = Bookmark.class))), + @ApiResponse(responseCode = "200", description = "Bookmark deleted successfully", content = @Content(schema = @Schema(implementation = org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model.Bookmark.class))), @ApiResponse(responseCode = "404", description = "Experiment or bookmark not found", content = @Content(schema = @Schema(implementation = String.class))) }) public Response deleteBookmark( @@ -384,28 +306,21 @@ public Response deleteBookmark( return Response.status(Status.NOT_FOUND).entity(NO_SUCH_EXPERIMENT).build(); } - Map bookmarks = EXPERIMENT_BOOKMARKS.get(expUUID); - if (bookmarks == null || !bookmarks.containsKey(bookmarkUUID)) { + IFile editorFile = TmfTraceManager.getInstance().getTraceEditorFile(experiment); + if (editorFile == null) { return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); } try { - IFolder bookmarkFolder = getBookmarkFolder(expUUID); - IFile bookmarkFile = bookmarkFolder.getFile(bookmarkUUID.toString() + ".bookmark"); //$NON-NLS-1$ - Bookmark deletedBookmark = bookmarks.remove(bookmarkUUID); - - if (bookmarkFile.exists()) { - bookmarkFile.delete(true, null); - } - - if (bookmarks.isEmpty()) { - EXPERIMENT_BOOKMARKS.remove(expUUID); - if (bookmarkFolder.exists()) { - bookmarkFolder.delete(true, null); - } + IMarker[] markers = findBookmarkMarkers(editorFile); + IMarker marker = findMarkerByUUID(markers, bookmarkUUID); + if (marker == null) { + return Response.status(Status.NOT_FOUND).entity(EndpointConstants.BOOKMARK_NOT_FOUND).build(); } - return Response.ok(deletedBookmark).build(); + Bookmark bookmark = markerToBookmark(marker); + marker.delete(); + return Response.ok(bookmark).build(); } catch (CoreException e) { Activator.getInstance().logError("Failed to delete bookmark", e); //$NON-NLS-1$ return Response.status(Status.INTERNAL_SERVER_ERROR).build(); @@ -413,36 +328,69 @@ public Response deleteBookmark( } /** - * Gets the Eclipse resource folder for the bookmark. - * - * @param expUUID - * UUID of the experiment - * @return The Eclipse resource folder - * - * @throws CoreException - * if an error occurs + * Find all bookmark markers in the editor file + */ + private static IMarker[] findBookmarkMarkers(IFile editorFile) throws CoreException { + return editorFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO); + } + + /** + * Convert a marker to a Bookmark object */ - private static @NonNull IFolder getBookmarkFolder(UUID expUUID) throws CoreException { - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IProject project = root.getProject(TmfCommonConstants.DEFAULT_TRACE_PROJECT_NAME); - project.refreshLocal(IResource.DEPTH_INFINITE, null); - IFolder bookmarksFolder = project.getFolder(BOOKMARKS_FOLDER); - return Objects.requireNonNull(bookmarksFolder.getFolder(expUUID.toString())); + private static Bookmark markerToBookmark(IMarker marker) throws CoreException { + String uuid = marker.getAttribute(BOOKMARK_UUID, (String) null); + if (uuid == null) { + return null; + } + + String name = marker.getAttribute(BOOKMARK_NAME).toString(); + long start = Long.parseLong(marker.getAttribute(BOOKMARK_START).toString()); + long end = Long.parseLong(marker.getAttribute(BOOKMARK_END).toString()); + + return new Bookmark(UUID.fromString(uuid), name, start, end); + } + + /** + * Find a specific bookmark marker by UUID + */ + private static IMarker findMarkerByUUID(IMarker[] markers, UUID bookmarkUUID) { + for (IMarker marker : markers) { + String uuid = marker.getAttribute(BOOKMARK_UUID, (String) null); + if (uuid != null && UUID.fromString(uuid).equals(bookmarkUUID)) { + return marker; + } + } + return null; + } + + /** + * Create a new marker with bookmark attributes + */ + private static void setMarkerAttributes(IMarker marker, Bookmark bookmark) throws CoreException { + marker.setAttribute(BOOKMARK_UUID, bookmark.getUUID().toString()); + marker.setAttribute(BOOKMARK_NAME, bookmark.getName()); + marker.setAttribute(BOOKMARK_START, Long.toString(bookmark.getStart())); + marker.setAttribute(BOOKMARK_END, Long.toString(bookmark.getEnd())); } - private static void createFolder(IFolder folder) throws CoreException { - if (!folder.exists()) { - if (folder.getParent() instanceof IFolder) { - createFolder((IFolder) folder.getParent()); + /** + * Convert list of markers to list of bookmarks + */ + private static List markersToBookmarks(IMarker[] markers) throws CoreException { + List bookmarks = new ArrayList<>(); + for (IMarker marker : markers) { + Bookmark bookmark = markerToBookmark(marker); + if (bookmark != null) { + bookmarks.add(bookmark); } - folder.create(true, true, null); } + return bookmarks; } /** - * Dispose method to be only called at server shutdown. + * Check if a bookmark with the same UUID already exists */ - public static void dispose() { - EXPERIMENT_BOOKMARKS.clear(); + private static boolean bookmarkExists(IMarker[] markers, UUID bookmarkUUID) { + return findMarkerByUUID(markers, bookmarkUUID) != null; } } \ No newline at end of file