From 1b96f8876d3250cd724ce8b487175ddcf0713e53 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 11 Aug 2020 10:26:31 -0500 Subject: [PATCH] workspaces incremental progress --- .../docsys/api/WebServiceObject.java | 15 +- .../docsys/core/DocServerStatusEndpoint.java | 1 + .../node/docsys/pages/ui/workspaces/index.vue | 32 +++++ .../rest/resources/WorkspacesEndpoint.java | 106 +++++++------- .../rest/services/WorkspaceService.java | 134 ++++++++++++++++++ .../engine/rest/services/package-info.java | 11 ++ .../rest/transfertypes/WorkspaceView.java | 12 +- ...orkspacesInfo.java => WorkspacesView.java} | 4 +- .../rest/transfertypes/package-info.java | 13 ++ scripts/release-prepare.sh | 3 + 10 files changed, 274 insertions(+), 57 deletions(-) create mode 100644 docsys/src/main/node/docsys/pages/ui/workspaces/index.vue create mode 100644 engine-rest/src/main/java/io/nosqlbench/engine/rest/services/WorkspaceService.java create mode 100644 engine-rest/src/main/java/io/nosqlbench/engine/rest/services/package-info.java rename engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/{WorkspacesInfo.java => WorkspacesView.java} (90%) create mode 100644 engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/package-info.java diff --git a/docsys/src/main/java/io/nosqlbench/docsys/api/WebServiceObject.java b/docsys/src/main/java/io/nosqlbench/docsys/api/WebServiceObject.java index 81a173d89..26b440aca 100644 --- a/docsys/src/main/java/io/nosqlbench/docsys/api/WebServiceObject.java +++ b/docsys/src/main/java/io/nosqlbench/docsys/api/WebServiceObject.java @@ -4,11 +4,18 @@ package io.nosqlbench.docsys.api; * Any class which is annotated with
{@code @Service(WebServiceObject.class)}
* will be found and loaded as a service. * - * For the methods used to configure these objects, consult - * jax-rs resources documentation + * For the methods used to configure these objects, consult the + * references below: * - * and - * Jax-RS API Docs + * @see Jersey jax-rs + * resources documentation + * + * @see JAX-RS: Java™ API for RESTful Web Services Version 2.1 + * Proposed Final Draft June 9, 2017 + ** + * @see Jax-RS API Docs */ public interface WebServiceObject { } diff --git a/docsys/src/main/java/io/nosqlbench/docsys/core/DocServerStatusEndpoint.java b/docsys/src/main/java/io/nosqlbench/docsys/core/DocServerStatusEndpoint.java index b8f88144d..5f6cd065c 100644 --- a/docsys/src/main/java/io/nosqlbench/docsys/core/DocServerStatusEndpoint.java +++ b/docsys/src/main/java/io/nosqlbench/docsys/core/DocServerStatusEndpoint.java @@ -17,6 +17,7 @@ import javax.ws.rs.core.MediaType; @Singleton @Path("_") public class DocServerStatusEndpoint implements WebServiceObject { + private final static Logger logger = LogManager.getLogger(DocServerStatusEndpoint.class); diff --git a/docsys/src/main/node/docsys/pages/ui/workspaces/index.vue b/docsys/src/main/node/docsys/pages/ui/workspaces/index.vue new file mode 100644 index 000000000..4698fdbd9 --- /dev/null +++ b/docsys/src/main/node/docsys/pages/ui/workspaces/index.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/WorkspacesEndpoint.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/WorkspacesEndpoint.java index f6de5dd8e..ecac6fa5c 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/WorkspacesEndpoint.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/WorkspacesEndpoint.java @@ -1,93 +1,103 @@ package io.nosqlbench.engine.rest.resources; import io.nosqlbench.docsys.api.WebServiceObject; -import io.nosqlbench.engine.rest.domain.WorkSpace; +import io.nosqlbench.engine.rest.services.WorkspaceService; import io.nosqlbench.engine.rest.transfertypes.WorkspaceView; -import io.nosqlbench.engine.rest.transfertypes.WorkspacesInfo; import io.nosqlbench.nb.annotations.Service; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import javax.inject.Singleton; import javax.ws.rs.*; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.io.IOException; -import java.nio.file.Files; +import java.nio.ByteBuffer; import java.nio.file.Paths; -import java.util.Optional; +import java.util.List; -@Path("/services/workspaces") +@Path("/services/workspaces/") @Singleton @Service(WebServiceObject.class) public class WorkspacesEndpoint implements WebServiceObject { - private final static java.nio.file.Path workspacesRoot = Paths.get("workspaces"); + private final static Logger logger = + LogManager.getLogger(WorkspacesEndpoint.class); + public static final String WORKSPACE_ROOT = "workspace_root"; + + @Context + private Configuration config; + + private final static java.nio.file.Path workspacesRoot = Paths.get("workspaces"); + private WorkspaceService svc; + + /** + * @return A list of workspaces as a + * {@link List} of {@link WorkspaceView} + */ @GET - @Path("") @Produces(MediaType.APPLICATION_JSON) - public Response listWorkspaces() { - WorkspacesInfo info = new WorkspacesInfo(workspacesRoot); - return Response.ok(info).build(); + public Response getWorkspacesInfo() { + List wsviews = getSvc().getWorkspaceViews(); + return Response.ok(wsviews).build(); } @GET @Path("{workspace}") @Produces(MediaType.APPLICATION_JSON) - public Response listWorkspace(@PathParam("workspace") String workspace) { - WorkspaceView view = new WorkspaceView(workspace); - return Response.ok(view).build(); + public Response getWorkspaceInfo(@PathParam("workspace") String workspace) { + WorkspaceView workpaceView = getSvc().getWorkspaceView(workspace); + return Response.ok(workpaceView).build(); + } + + @POST + @Path("{workspaceName}/upload/{filepath}") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Response uploadFileIntoWorkspace( + ) { + return Response.ok().build(); } @POST @Path("{workspaceName}/{filepath}") - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public Response putFile( - @PathParam("workspaceName") String workspaceName, - @PathParam("filepath") String filename + public Response putFileInWorkspace( + @PathParam("workspaceName") String workspaceName, + @PathParam("filepath") String filename, + ByteBuffer content ) { - WorkSpace workspace = getWorkspace(workspaceName); - Optional puttedFile = workspace.put(workspace); - if (puttedFile.isPresent()) { + try { + getSvc().putFile(workspaceName, filename, content); return Response.ok().build(); - } else { + } catch (Exception e) { return Response.status(Response.Status.BAD_REQUEST).build(); } } @GET @Path("{workspaceName}/{filename}") - public Response getFile( - @PathParam("workspaceName") String workspaceName, - @PathParam("filename") String filename) { + public Response getFileInWorkspace( + @PathParam("workspaceName") String workspaceName, + @PathParam("filename") String filename) { - WorkSpace workSpace = new WorkSpace(workspacesRoot, workspaceName); - Optional optFile = workSpace.get(filename); - - if (optFile.isPresent()) { - try { - java.nio.file.Path filepath = optFile.get(); - String contentType = Files.probeContentType(filepath); - MediaType mediaType = MediaType.valueOf(contentType); - - byte[] bytes = Files.readAllBytes(filepath); - return Response.ok(bytes, mediaType).build(); - } catch (IOException e) { - throw new RuntimeException(e); + try { + WorkspaceService.FileInfo fileinfo = getSvc().readFile(workspaceName, filename); + if (fileinfo != null) { + return Response.ok(fileinfo.getContent(), fileinfo.getMediaType()).build(); + } else { + return Response.noContent().status(Response.Status.NOT_FOUND).build(); } - } else { - return Response.noContent().status(Response.Status.NOT_FOUND).build(); + } catch (Exception e) { + return Response.serverError().entity(e.getMessage()).build(); } - } - - private WorkSpace getWorkspace(String workspace) { - if (!workspace.matches("[a-zA-Z][a-zA-Z0-9]+")) { - throw new RuntimeException("Workspaces must start with an alphabetic" + - " character, and contain only letters and numbers."); + private WorkspaceService getSvc() { + if (svc == null) { + svc = new WorkspaceService(config.getProperties().get(WORKSPACE_ROOT)); } - WorkSpace workSpace = new WorkSpace(workspacesRoot, workspace); - return workSpace; + return svc; } } diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/WorkspaceService.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/WorkspaceService.java new file mode 100644 index 000000000..6c937f4e5 --- /dev/null +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/WorkspaceService.java @@ -0,0 +1,134 @@ +package io.nosqlbench.engine.rest.services; + +import io.nosqlbench.engine.rest.transfertypes.WorkspaceView; + +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; + +public class WorkspaceService { + private final Path root; + public static String DEFAULT = "default"; + + public WorkspaceService(Object root) { + if (root instanceof Path) { + this.root = (Path) root; + } else if (root instanceof CharSequence) { + this.root = Paths.get(((CharSequence) root).toString()); + } else if (root == null) { + this.root = Paths.get(System.getProperty("user.dir"), + "workspaces"); + try { + Files.createDirectories(this.root); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException("Unable to use workspaces root " + + "path of type " + root.getClass().getCanonicalName()); + } + createDefaultIfNotExist(); + } + + private void createDefaultIfNotExist() { + getWorkspaceView(DEFAULT); + } + + public List getWorkspaceViews() { + List views = new ArrayList<>(); + DirectoryStream wsrEntries = null; + try { + wsrEntries = Files.newDirectoryStream(root); + } catch (IOException e) { + throw new RuntimeException(e); + } + + for (Path entry : wsrEntries) { + views.add(new WorkspaceView(entry)); + } + return views; + } + + public WorkspaceView getWorkspaceView(String workspace) { + if (!workspace.matches("[a-zA-Z][a-zA-Z0-9]+")) { + throw new RuntimeException("Workspaces must start with an alphabetic" + + " character, and contain only letters and numbers."); + } + + Path wspath = root.resolve(Paths.get(workspace)); + if (!Files.exists(wspath)) { + try { + Files.createDirectories(wspath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return new WorkspaceView(wspath); + } + + public void putFile(String workspaceName, String filename, ByteBuffer content) { + Path toWrite = root.resolve(workspaceName).resolve(filename); + try { + Files.write(toWrite, content.array(), + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE, + StandardOpenOption.WRITE); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + /** + * Read the bytes of the named file in the named workspace. + * + * @param workspaceName The workspace name to look in for the file + * @param filename The filename within the workspace to read + * @return null if the file is not found + * @throws RuntimeException if the file was found but could not be + * read. + */ + public FileInfo readFile(String workspaceName, String filename) { + Path filePath = workspacePath(workspaceName).resolve(filename); + if (Files.exists(filePath)) { + return new FileInfo(filePath); + } else { + return null; + } + } + + private Path workspacePath(String workspaceName) { + return root.resolve(workspaceName); + } + + public final static class FileInfo { + private final Path path; + + public FileInfo(Path path) { + this.path = path; + } + + public MediaType getMediaType() { + try { + String contentType = Files.probeContentType(path); + MediaType mediaType = MediaType.valueOf(contentType); + return mediaType; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public ByteBuffer getContent() { + byte[] bytes = new byte[0]; + try { + bytes = Files.readAllBytes(path); + } catch (IOException e) { + throw new RuntimeException(e); + } + return ByteBuffer.wrap(bytes); + } + } +} diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/package-info.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/package-info.java new file mode 100644 index 000000000..cc9d0da9d --- /dev/null +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/package-info.java @@ -0,0 +1,11 @@ +/** + * Classes in this package are meant to provide a basic + * internal service facade to be used by endpoints. + * This simplifies endpoint implementations and facilitates DRY + * implementations. + * + * These implementations should only expose primitive types, + * collections of primitive types, or views of transfer types + * as implemented in that package. + */ +package io.nosqlbench.engine.rest.services; diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspaceView.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspaceView.java index d05dfa816..6e76114ce 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspaceView.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspaceView.java @@ -1,10 +1,16 @@ package io.nosqlbench.engine.rest.transfertypes; +import java.nio.file.Path; + public class WorkspaceView { - private final String workspace; + private final Path workspaceRoot; - public WorkspaceView(String workspace) { - this.workspace = workspace; + public WorkspaceView(Path workspaceRoot) { + this.workspaceRoot = workspaceRoot; + } + + public String getName() { + return workspaceRoot.getFileName().toString(); } } diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesInfo.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesView.java similarity index 90% rename from engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesInfo.java rename to engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesView.java index c75c68363..9713e60be 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesInfo.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/WorkspacesView.java @@ -7,10 +7,10 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -public class WorkspacesInfo { +public class WorkspacesView { private Path workspacesRoot; - public WorkspacesInfo(Path workspacesRoot) { + public WorkspacesView(Path workspacesRoot) { this.workspacesRoot = workspacesRoot; } diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/package-info.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/package-info.java new file mode 100644 index 000000000..31e39bd03 --- /dev/null +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/package-info.java @@ -0,0 +1,13 @@ +/** + * Types in this package are meant to provide a mapping + * between internal state and external views. Specifically, + * these types should only include details which are meant to + * be shared by endpoints. This can be achieved by wrapping + * internal state and exposing only the visible properties, or + * it can be done by implementing types which are built from + * common internal types. + * + * Service objects in the services package should only provide + * primitive values or the view types from this package. + */ +package io.nosqlbench.engine.rest.transfertypes; diff --git a/scripts/release-prepare.sh b/scripts/release-prepare.sh index 11a363d13..033b461a1 100755 --- a/scripts/release-prepare.sh +++ b/scripts/release-prepare.sh @@ -27,3 +27,6 @@ echo "Do mvn release:prepare..." #mvn $MAVEN_REPO_LOCAL --batch-mode --global-settings release.xml -Dusername=$GITHUB_ACCESS_TOKEN release:prepare mvn --batch-mode --global-settings release.xml -Dusername=$GITHUB_ACCESS_TOKEN clean release:prepare -DdevelopmentVersion=${NEXT_SNAPSHOT} -DreleaseVersion=${RELEASE_VERSION} +echo "files after release:prepare..." +pwd +ls -l