From 584f0f92df310348b996b669c5070718546a6fb0 Mon Sep 17 00:00:00 2001 From: Tanish Azad Date: Fri, 24 May 2024 05:03:44 +0530 Subject: [PATCH] added custom spotless config (#28) * update * rewrite if * tj bot spotless config --- .../org/togetherjava/jshellapi/Config.java | 18 +- .../jshellapi/dto/JShellEvalAbortion.java | 5 +- .../dto/JShellEvalAbortionCause.java | 14 +- .../jshellapi/dto/JShellResult.java | 7 +- .../jshellapi/dto/JShellResultWithId.java | 3 +- .../jshellapi/dto/JShellSnippetResult.java | 5 +- .../jshellapi/exceptions/DockerException.java | 5 +- .../jshellapi/rest/JShellController.java | 65 ++-- .../jshellapi/service/DockerService.java | 133 ++++--- .../jshellapi/service/JShellService.java | 106 +++--- .../service/JShellSessionService.java | 98 +++-- .../jshellapi/service/SessionInfo.java | 31 +- .../service/StartupScriptsService.java | 4 +- .../togetherjava/jshellapi/service/Utils.java | 8 +- .../jshell/wrapper/EvalResult.java | 3 +- .../jshell/wrapper/JShellEvalAbortion.java | 5 +- .../wrapper/JShellEvalAbortionCause.java | 14 +- .../jshell/wrapper/JShellWrapper.java | 103 +++--- .../jshell/wrapper/StringOutputStream.java | 15 +- .../jshell/wrapper/TimeoutWatcher.java | 19 +- .../java/JShellWrapperStartupScriptTest.java | 15 +- .../src/test/java/JShellWrapperTest.java | 167 ++++----- .../src/test/java/StringInputStream.java | 3 +- .../src/test/java/StringOutputStreamTest.java | 13 +- build.gradle | 19 +- spotless.xml | 336 ++++++++++++++++++ 26 files changed, 711 insertions(+), 503 deletions(-) create mode 100644 spotless.xml diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/Config.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/Config.java index 3e2558b..4c337e9 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/Config.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/Config.java @@ -4,19 +4,11 @@ import org.springframework.lang.Nullable; @ConfigurationProperties("jshellapi") -public record Config( - long regularSessionTimeoutSeconds, - long oneTimeSessionTimeoutSeconds, - long evalTimeoutSeconds, - long evalTimeoutValidationLeeway, - int sysOutCharLimit, - long maxAliveSessions, - int dockerMaxRamMegaBytes, - double dockerCPUsUsage, - @Nullable String dockerCPUSetCPUs, - long schedulerSessionKillScanRateSeconds, - long dockerResponseTimeout, - long dockerConnectionTimeout) { +public record Config(long regularSessionTimeoutSeconds, long oneTimeSessionTimeoutSeconds, + long evalTimeoutSeconds, long evalTimeoutValidationLeeway, int sysOutCharLimit, + long maxAliveSessions, int dockerMaxRamMegaBytes, double dockerCPUsUsage, + @Nullable String dockerCPUSetCPUs, long schedulerSessionKillScanRateSeconds, + long dockerResponseTimeout, long dockerConnectionTimeout) { public Config { if (regularSessionTimeoutSeconds <= 0) throw new IllegalArgumentException("Invalid value " + regularSessionTimeoutSeconds); diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortion.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortion.java index 3a6f93b..ba04fbd 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortion.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortion.java @@ -1,4 +1,5 @@ package org.togetherjava.jshellapi.dto; -public record JShellEvalAbortion( - String sourceCause, String remainingSource, JShellEvalAbortionCause cause) {} +public record JShellEvalAbortion(String sourceCause, String remainingSource, + JShellEvalAbortionCause cause) { +} diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortionCause.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortionCause.java index 2925ae5..0fed77b 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortionCause.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellEvalAbortionCause.java @@ -9,15 +9,19 @@ public sealed interface JShellEvalAbortionCause { @JsonTypeName("TIMEOUT") - record TimeoutAbortionCause() implements JShellEvalAbortionCause {} + record TimeoutAbortionCause() implements JShellEvalAbortionCause { + } @JsonTypeName("UNCAUGHT_EXCEPTION") - record UnhandledExceptionAbortionCause(String exceptionClass, String exceptionMessage) - implements JShellEvalAbortionCause {} + record UnhandledExceptionAbortionCause(String exceptionClass, + String exceptionMessage) implements JShellEvalAbortionCause { + } @JsonTypeName("COMPILE_TIME_ERROR") - record CompileTimeErrorAbortionCause(List errors) implements JShellEvalAbortionCause {} + record CompileTimeErrorAbortionCause(List errors) implements JShellEvalAbortionCause { + } @JsonTypeName("SYNTAX_ERROR") - record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause {} + record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause { + } } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResult.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResult.java index 1fac3d7..6ab2bc6 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResult.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResult.java @@ -4,11 +4,8 @@ import java.util.List; -public record JShellResult( - List snippetsResults, - @Nullable JShellEvalAbortion abortion, - boolean stdoutOverflow, - String stdout) { +public record JShellResult(List snippetsResults, + @Nullable JShellEvalAbortion abortion, boolean stdoutOverflow, String stdout) { public JShellResult { snippetsResults = List.copyOf(snippetsResults); } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResultWithId.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResultWithId.java index e0e4d6f..be9c7a3 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResultWithId.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellResultWithId.java @@ -1,3 +1,4 @@ package org.togetherjava.jshellapi.dto; -public record JShellResultWithId(String id, JShellResult result) {} +public record JShellResultWithId(String id, JShellResult result) { +} diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellSnippetResult.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellSnippetResult.java index 13c0d3a..3409ccb 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellSnippetResult.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/dto/JShellSnippetResult.java @@ -2,5 +2,6 @@ import org.springframework.lang.Nullable; -public record JShellSnippetResult( - SnippetStatus status, SnippetType type, int id, String source, @Nullable String result) {} +public record JShellSnippetResult(SnippetStatus status, SnippetType type, int id, String source, + @Nullable String result) { +} diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/exceptions/DockerException.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/exceptions/DockerException.java index 3c7e093..331ff36 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/exceptions/DockerException.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/exceptions/DockerException.java @@ -19,10 +19,7 @@ public DockerException(Throwable cause) { super(cause); } - public DockerException( - String message, - Throwable cause, - boolean enableSuppression, + public DockerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/rest/JShellController.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/rest/JShellController.java index 63d2011..2c60570 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/rest/JShellController.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/rest/JShellController.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; + import org.togetherjava.jshellapi.dto.JShellResult; import org.togetherjava.jshellapi.dto.JShellResultWithId; import org.togetherjava.jshellapi.exceptions.DockerException; @@ -21,64 +22,45 @@ public class JShellController { private StartupScriptsService startupScriptsService; @PostMapping("/eval/{id}") - public JShellResult eval( - @PathVariable String id, + public JShellResult eval(@PathVariable String id, @RequestParam(required = false) StartupScriptId startupScriptId, - @RequestBody String code) - throws DockerException { + @RequestBody String code) throws DockerException { validateId(id); return service.session(id, startupScriptId) - .eval(code) - .orElseThrow( - () -> - new ResponseStatusException( - HttpStatus.CONFLICT, "An operation is already running")); + .eval(code) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT, + "An operation is already running")); } @PostMapping("/eval") - public JShellResultWithId eval( - @RequestParam(required = false) StartupScriptId startupScriptId, - @RequestBody String code) - throws DockerException { + public JShellResultWithId eval(@RequestParam(required = false) StartupScriptId startupScriptId, + @RequestBody String code) throws DockerException { JShellService jShellService = service.session(startupScriptId); - return new JShellResultWithId( - jShellService.id(), - jShellService - .eval(code) - .orElseThrow( - () -> - new ResponseStatusException( - HttpStatus.CONFLICT, - "An operation is already running"))); + return new JShellResultWithId(jShellService.id(), + jShellService.eval(code) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT, + "An operation is already running"))); } @PostMapping("/single-eval") - public JShellResult singleEval( - @RequestParam(required = false) StartupScriptId startupScriptId, - @RequestBody String code) - throws DockerException { + public JShellResult singleEval(@RequestParam(required = false) StartupScriptId startupScriptId, + @RequestBody String code) throws DockerException { JShellService jShellService = service.oneTimeSession(startupScriptId); - return jShellService - .eval(code) - .orElseThrow( - () -> - new ResponseStatusException( - HttpStatus.CONFLICT, "An operation is already running")); + return jShellService.eval(code) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT, + "An operation is already running")); } @GetMapping("/snippets/{id}") - public List snippets( - @PathVariable String id, @RequestParam(required = false) boolean includeStartupScript) - throws DockerException { + public List snippets(@PathVariable String id, + @RequestParam(required = false) boolean includeStartupScript) throws DockerException { validateId(id); if (!service.hasSession(id)) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Id " + id + " not found"); return service.session(id, null) - .snippets(includeStartupScript) - .orElseThrow( - () -> - new ResponseStatusException( - HttpStatus.CONFLICT, "An operation is already running")); + .snippets(includeStartupScript) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.CONFLICT, + "An operation is already running")); } @DeleteMapping("/{id}") @@ -106,8 +88,7 @@ public void setStartupScriptsService(StartupScriptsService startupScriptsService private static void validateId(String id) throws ResponseStatusException { if (!id.matches("[a-zA-Z0-9][a-zA-Z0-9_.-]+")) { - throw new ResponseStatusException( - HttpStatus.BAD_REQUEST, + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Id " + id + " doesn't match the regex [a-zA-Z0-9][a-zA-Z0-9_.-]+"); } } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/DockerService.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/DockerService.java index e7fb651..c5abcbb 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/DockerService.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/DockerService.java @@ -7,12 +7,12 @@ import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; + import org.togetherjava.jshellapi.Config; import java.io.*; @@ -33,20 +33,20 @@ public DockerService(Config config) { DefaultDockerClientConfig clientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); ApacheDockerHttpClient httpClient = - new ApacheDockerHttpClient.Builder() - .dockerHost(clientConfig.getDockerHost()) - .sslConfig(clientConfig.getSSLConfig()) - .responseTimeout(Duration.ofSeconds(config.dockerResponseTimeout())) - .connectionTimeout(Duration.ofSeconds(config.dockerConnectionTimeout())) - .build(); + new ApacheDockerHttpClient.Builder().dockerHost(clientConfig.getDockerHost()) + .sslConfig(clientConfig.getSSLConfig()) + .responseTimeout(Duration.ofSeconds(config.dockerResponseTimeout())) + .connectionTimeout(Duration.ofSeconds(config.dockerConnectionTimeout())) + .build(); this.client = DockerClientImpl.getInstance(clientConfig, httpClient); cleanupLeftovers(WORKER_UNIQUE_ID); } private void cleanupLeftovers(UUID currentId) { - for (Container container : - client.listContainersCmd().withLabelFilter(Set.of(WORKER_LABEL)).exec()) { + for (Container container : client.listContainersCmd() + .withLabelFilter(Set.of(WORKER_LABEL)) + .exec()) { String containerHumanName = container.getId() + " " + Arrays.toString(container.getNames()); LOGGER.info("Found worker container '{}'", containerHumanName); @@ -57,50 +57,44 @@ private void cleanupLeftovers(UUID currentId) { } } - public String spawnContainer( - long maxMemoryMegs, - long cpus, - @Nullable String cpuSetCpus, - String name, - Duration evalTimeout, - long sysoutLimit) - throws InterruptedException { + public String spawnContainer(long maxMemoryMegs, long cpus, @Nullable String cpuSetCpus, + String name, Duration evalTimeout, long sysoutLimit) throws InterruptedException { String imageName = "togetherjava.org:5001/togetherjava/jshellwrapper"; - boolean presentLocally = - client.listImagesCmd().withFilter("reference", List.of(imageName)).exec().stream() - .flatMap(it -> Arrays.stream(it.getRepoTags())) - .anyMatch(it -> it.endsWith(":master")); + boolean presentLocally = client.listImagesCmd() + .withFilter("reference", List.of(imageName)) + .exec() + .stream() + .flatMap(it -> Arrays.stream(it.getRepoTags())) + .anyMatch(it -> it.endsWith(":master")); if (!presentLocally) { client.pullImageCmd(imageName) - .withTag("master") - .exec(new PullImageResultCallback()) - .awaitCompletion(5, TimeUnit.MINUTES); + .withTag("master") + .exec(new PullImageResultCallback()) + .awaitCompletion(5, TimeUnit.MINUTES); } return client.createContainerCmd(imageName + ":master") - .withHostConfig( - HostConfig.newHostConfig() - .withAutoRemove(true) - .withInit(true) - .withCapDrop(Capability.ALL) - .withNetworkMode("none") - .withPidsLimit(2000L) - .withReadonlyRootfs(true) - .withMemory(maxMemoryMegs * 1024 * 1024) - .withCpuCount(cpus) - .withCpusetCpus(cpuSetCpus)) - .withStdinOpen(true) - .withAttachStdin(true) - .withAttachStderr(true) - .withAttachStdout(true) - .withEnv( - "evalTimeoutSeconds=" + evalTimeout.toSeconds(), - "sysOutCharLimit=" + sysoutLimit) - .withLabels(Map.of(WORKER_LABEL, WORKER_UNIQUE_ID.toString())) - .withName(name) - .exec() - .getId(); + .withHostConfig(HostConfig.newHostConfig() + .withAutoRemove(true) + .withInit(true) + .withCapDrop(Capability.ALL) + .withNetworkMode("none") + .withPidsLimit(2000L) + .withReadonlyRootfs(true) + .withMemory(maxMemoryMegs * 1024 * 1024) + .withCpuCount(cpus) + .withCpusetCpus(cpuSetCpus)) + .withStdinOpen(true) + .withAttachStdin(true) + .withAttachStderr(true) + .withAttachStdout(true) + .withEnv("evalTimeoutSeconds=" + evalTimeout.toSeconds(), + "sysOutCharLimit=" + sysoutLimit) + .withLabels(Map.of(WORKER_LABEL, WORKER_UNIQUE_ID.toString())) + .withName(name) + .exec() + .getId(); } public InputStream startAndAttachToContainer(String containerId, InputStream stdin) @@ -109,31 +103,28 @@ public InputStream startAndAttachToContainer(String containerId, InputStream std PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); client.attachContainerCmd(containerId) - .withLogs(true) - .withFollowStream(true) - .withStdOut(true) - .withStdErr(true) - .withStdIn(stdin) - .exec( - new ResultCallback.Adapter<>() { - @Override - public void onNext(Frame object) { - try { - String payloadString = - new String(object.getPayload(), StandardCharsets.UTF_8); - if (object.getStreamType() == StreamType.STDOUT) { - pipeOut.write(object.getPayload()); - } else { - LOGGER.warn( - "Received STDERR from container {}: {}", - containerId, - payloadString); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - }); + .withLogs(true) + .withFollowStream(true) + .withStdOut(true) + .withStdErr(true) + .withStdIn(stdin) + .exec(new ResultCallback.Adapter<>() { + @Override + public void onNext(Frame object) { + try { + String payloadString = + new String(object.getPayload(), StandardCharsets.UTF_8); + if (object.getStreamType() == StreamType.STDOUT) { + pipeOut.write(object.getPayload()); + } else { + LOGGER.warn("Received STDERR from container {}: {}", containerId, + payloadString); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + }); client.startContainerCmd(containerId).exec(); return pipeIn; diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellService.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellService.java index e712f55..30631a9 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellService.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellService.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; + import org.togetherjava.jshellapi.dto.*; import org.togetherjava.jshellapi.exceptions.DockerException; @@ -30,20 +31,10 @@ public class JShellService implements Closeable { private final DockerService dockerService; private final int startupScriptSize; - public JShellService( - DockerService dockerService, - JShellSessionService sessionService, - String id, - long timeout, - boolean renewable, - long evalTimeout, - long evalTimeoutValidationLeeway, - int sysOutCharLimit, - int maxMemory, - double cpus, - @Nullable String cpuSetCpus, - String startupScript) - throws DockerException { + public JShellService(DockerService dockerService, JShellSessionService sessionService, + String id, long timeout, boolean renewable, long evalTimeout, + long evalTimeoutValidationLeeway, int sysOutCharLimit, int maxMemory, double cpus, + @Nullable String cpuSetCpus, String startupScript) throws DockerException { this.dockerService = dockerService; this.sessionService = sessionService; this.id = id; @@ -57,18 +48,11 @@ public JShellService( throw new DockerException("The session isn't completely destroyed, try again later."); } try { - String containerId = - dockerService.spawnContainer( - maxMemory, - (long) Math.ceil(cpus), - cpuSetCpus, - containerName(), - Duration.ofSeconds(evalTimeout), - sysOutCharLimit); + String containerId = dockerService.spawnContainer(maxMemory, (long) Math.ceil(cpus), + cpuSetCpus, containerName(), Duration.ofSeconds(evalTimeout), sysOutCharLimit); PipedInputStream containerInput = new PipedInputStream(); - this.writer = - new BufferedWriter( - new OutputStreamWriter(new PipedOutputStream(containerInput))); + this.writer = new BufferedWriter( + new OutputStreamWriter(new PipedOutputStream(containerInput))); InputStream containerOutput = dockerService.startAndAttachToContainer(containerId, containerInput); reader = new BufferedReader(new InputStreamReader(containerOutput)); @@ -96,7 +80,8 @@ public Optional eval(String code) throws DockerException { } updateLastTimeout(); sessionService.scheduleEvalTimeoutValidation(id, evalTimeout + evalTimeoutValidationLeeway); - if (!code.endsWith("\n")) code += '\n'; + if (!code.endsWith("\n")) + code += '\n'; try { writer.write("eval"); writer.newLine(); @@ -121,16 +106,10 @@ private JShellResult readResult() throws IOException, NumberFormatException, Doc final int snippetsCount = Integer.parseInt(reader.readLine()); List snippetResults = new ArrayList<>(); for (int i = 0; i < snippetsCount; i++) { - SnippetStatus status = - Utils.nameOrElseThrow( - SnippetStatus.class, - reader.readLine(), - name -> new DockerException(name + " isn't an enum constant")); - SnippetType type = - Utils.nameOrElseThrow( - SnippetType.class, - reader.readLine(), - name -> new DockerException(name + " isn't an enum constant")); + SnippetStatus status = Utils.nameOrElseThrow(SnippetStatus.class, reader.readLine(), + name -> new DockerException(name + " isn't an enum constant")); + SnippetType type = Utils.nameOrElseThrow(SnippetType.class, reader.readLine(), + name -> new DockerException(name + " isn't an enum constant")); int snippetId = Integer.parseInt(reader.readLine()); String source = cleanCode(reader.readLine()); String result = reader.readLine().transform(r -> r.equals("NONE") ? null : r); @@ -139,28 +118,25 @@ private JShellResult readResult() throws IOException, NumberFormatException, Doc JShellEvalAbortion abortion = null; String rawAbortionCause = reader.readLine(); if (!rawAbortionCause.isEmpty()) { - JShellEvalAbortionCause abortionCause = - switch (rawAbortionCause) { - case "TIMEOUT" -> new JShellEvalAbortionCause.TimeoutAbortionCause(); - case "UNCAUGHT_EXCEPTION" -> { - String[] split = reader.readLine().split(":"); - yield new JShellEvalAbortionCause.UnhandledExceptionAbortionCause( - split[0], split[1]); - } - case "COMPILE_TIME_ERROR" -> { - int errorCount = Integer.parseInt(reader.readLine()); - List errors = new ArrayList<>(); - for (int i = 0; i < errorCount; i++) { - errors.add(desanitize(reader.readLine())); - } - yield new JShellEvalAbortionCause.CompileTimeErrorAbortionCause(errors); - } - case "SYNTAX_ERROR" -> - new JShellEvalAbortionCause.SyntaxErrorAbortionCause(); - default -> - throw new DockerException( - "Abortion cause " + rawAbortionCause + " doesn't exist"); - }; + JShellEvalAbortionCause abortionCause = switch (rawAbortionCause) { + case "TIMEOUT" -> new JShellEvalAbortionCause.TimeoutAbortionCause(); + case "UNCAUGHT_EXCEPTION" -> { + String[] split = reader.readLine().split(":"); + yield new JShellEvalAbortionCause.UnhandledExceptionAbortionCause(split[0], + split[1]); + } + case "COMPILE_TIME_ERROR" -> { + int errorCount = Integer.parseInt(reader.readLine()); + List errors = new ArrayList<>(); + for (int i = 0; i < errorCount; i++) { + errors.add(desanitize(reader.readLine())); + } + yield new JShellEvalAbortionCause.CompileTimeErrorAbortionCause(errors); + } + case "SYNTAX_ERROR" -> new JShellEvalAbortionCause.SyntaxErrorAbortionCause(); + default -> throw new DockerException( + "Abortion cause " + rawAbortionCause + " doesn't exist"); + }; String causeSource = cleanCode(reader.readLine()); String remainingSource = cleanCode(reader.readLine()); abortion = new JShellEvalAbortion(causeSource, remainingSource, abortionCause); @@ -189,10 +165,8 @@ public Optional> snippets(boolean includeStartupScript) throws Dock while (!(snippet = reader.readLine()).isEmpty()) { snippets.add(cleanCode(snippet)); } - return Optional.of( - includeStartupScript - ? snippets - : snippets.subList(startupScriptSize, snippets.size())); + return Optional.of(includeStartupScript ? snippets + : snippets.subList(startupScriptSize, snippets.size())); } catch (IOException ex) { LOGGER.warn("Unexpected error.", ex); close(); @@ -208,9 +182,8 @@ public String containerName() { public boolean isInvalidEvalTimeout() { return doingOperation - && lastTimeoutUpdate - .plusSeconds(evalTimeout + evalTimeoutValidationLeeway) - .isBefore(Instant.now()); + && lastTimeoutUpdate.plusSeconds(evalTimeout + evalTimeoutValidationLeeway) + .isBefore(Instant.now()); } public boolean shouldDie() { @@ -290,7 +263,8 @@ private void checkContainerOK() throws DockerException { } private synchronized boolean tryStartOperation() { - if (doingOperation) return false; + if (doingOperation) + return false; doingOperation = true; return true; } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellSessionService.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellSessionService.java index 5aacbb8..c6411ef 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellSessionService.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/JShellSessionService.java @@ -7,6 +7,7 @@ import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; + import org.togetherjava.jshellapi.Config; import org.togetherjava.jshellapi.exceptions.DockerException; @@ -26,31 +27,30 @@ public class JShellSessionService { private void initScheduler() { scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.scheduleAtFixedRate( - () -> { - jshellSessions.keySet().stream() - .filter(id -> jshellSessions.get(id).isClosed()) - .forEach(this::notifyDeath); - List toDie = - jshellSessions.keySet().stream() - .filter(id -> jshellSessions.get(id).shouldDie()) - .toList(); - for (String id : toDie) { - try { - deleteSession(id); - } catch (DockerException ex) { - LOGGER.error("Unexpected error when deleting session.", ex); - } - } - }, - config.schedulerSessionKillScanRateSeconds(), - config.schedulerSessionKillScanRateSeconds(), - TimeUnit.SECONDS); + scheduler.scheduleAtFixedRate(() -> { + jshellSessions.keySet() + .stream() + .filter(id -> jshellSessions.get(id).isClosed()) + .forEach(this::notifyDeath); + List toDie = jshellSessions.keySet() + .stream() + .filter(id -> jshellSessions.get(id).shouldDie()) + .toList(); + for (String id : toDie) { + try { + deleteSession(id); + } catch (DockerException ex) { + LOGGER.error("Unexpected error when deleting session.", ex); + } + } + }, config.schedulerSessionKillScanRateSeconds(), + config.schedulerSessionKillScanRateSeconds(), TimeUnit.SECONDS); } void notifyDeath(String id) { JShellService shellService = jshellSessions.remove(id); - if (shellService == null) return; + if (shellService == null) + return; if (!shellService.isClosed()) { LOGGER.error("JShell Service isn't dead when it should for id {}.", id); } @@ -70,16 +70,14 @@ public JShellService session(String id, @Nullable StartupScriptId startupScriptI } public JShellService session(@Nullable StartupScriptId startupScriptId) throws DockerException { - return createSession( - new SessionInfo( - UUID.randomUUID().toString(), false, startupScriptId, false, config)); + return createSession(new SessionInfo(UUID.randomUUID().toString(), false, startupScriptId, + false, config)); } public JShellService oneTimeSession(@Nullable StartupScriptId startupScriptId) throws DockerException { - return createSession( - new SessionInfo( - UUID.randomUUID().toString(), false, startupScriptId, true, config)); + return createSession(new SessionInfo(UUID.randomUUID().toString(), false, startupScriptId, + true, config)); } public void deleteSession(String id) throws DockerException { @@ -90,30 +88,20 @@ public void deleteSession(String id) throws DockerException { private synchronized JShellService createSession(SessionInfo sessionInfo) throws DockerException { - if (hasSession( - sessionInfo - .id())) { // Just in case race condition happens just before createSession + // Just in case race condition happens just before createSession + if (hasSession(sessionInfo.id())) { return jshellSessions.get(sessionInfo.id()); } if (jshellSessions.size() >= config.maxAliveSessions()) { - throw new ResponseStatusException( - HttpStatus.TOO_MANY_REQUESTS, "Too many sessions, try again later :(."); + throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS, + "Too many sessions, try again later :(."); } LOGGER.info("Creating session : {}.", sessionInfo); - JShellService service = - new JShellService( - dockerService, - this, - sessionInfo.id(), - sessionInfo.sessionTimeout(), - sessionInfo.renewable(), - sessionInfo.evalTimeout(), - sessionInfo.evalTimeoutValidationLeeway(), - sessionInfo.sysOutCharLimit(), - config.dockerMaxRamMegaBytes(), - config.dockerCPUsUsage(), - config.dockerCPUSetCPUs(), - startupScriptsService.get(sessionInfo.startupScriptId())); + JShellService service = new JShellService(dockerService, this, sessionInfo.id(), + sessionInfo.sessionTimeout(), sessionInfo.renewable(), sessionInfo.evalTimeout(), + sessionInfo.evalTimeoutValidationLeeway(), sessionInfo.sysOutCharLimit(), + config.dockerMaxRamMegaBytes(), config.dockerCPUsUsage(), config.dockerCPUSetCPUs(), + startupScriptsService.get(sessionInfo.startupScriptId())); jshellSessions.put(sessionInfo.id(), service); return service; } @@ -127,16 +115,14 @@ private synchronized JShellService createSession(SessionInfo sessionInfo) * @param timeSeconds the time to schedule */ public void scheduleEvalTimeoutValidation(String id, long timeSeconds) { - scheduler.schedule( - () -> { - JShellService service = jshellSessions.get(id); - if (service == null) return; - if (service.isInvalidEvalTimeout()) { - service.close(); - } - }, - timeSeconds, - TimeUnit.SECONDS); + scheduler.schedule(() -> { + JShellService service = jshellSessions.get(id); + if (service == null) + return; + if (service.isInvalidEvalTimeout()) { + service.close(); + } + }, timeSeconds, TimeUnit.SECONDS); } @Autowired diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/SessionInfo.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/SessionInfo.java index 631cd87..fb5e7f6 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/SessionInfo.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/SessionInfo.java @@ -1,31 +1,16 @@ package org.togetherjava.jshellapi.service; import org.springframework.lang.Nullable; + import org.togetherjava.jshellapi.Config; -public record SessionInfo( - String id, - long sessionTimeout, - boolean renewable, - long evalTimeout, - long evalTimeoutValidationLeeway, - int sysOutCharLimit, +public record SessionInfo(String id, long sessionTimeout, boolean renewable, long evalTimeout, + long evalTimeoutValidationLeeway, int sysOutCharLimit, @Nullable StartupScriptId startupScriptId) { - public SessionInfo( - String id, - boolean renewable, - StartupScriptId startupScriptId, - boolean isOneTime, - Config config) { - this( - id, - isOneTime - ? config.oneTimeSessionTimeoutSeconds() - : config.regularSessionTimeoutSeconds(), - renewable, - config.evalTimeoutSeconds(), - config.evalTimeoutValidationLeeway(), - config.sysOutCharLimit(), - startupScriptId); + public SessionInfo(String id, boolean renewable, StartupScriptId startupScriptId, + boolean isOneTime, Config config) { + this(id, isOneTime ? config.oneTimeSessionTimeoutSeconds() + : config.regularSessionTimeoutSeconds(), renewable, config.evalTimeoutSeconds(), + config.evalTimeoutValidationLeeway(), config.sysOutCharLimit(), startupScriptId); } } diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/StartupScriptsService.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/StartupScriptsService.java index fd05211..a6cd5ce 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/StartupScriptsService.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/StartupScriptsService.java @@ -21,8 +21,8 @@ private StartupScriptsService() { for (StartupScriptId id : StartupScriptId.values()) { try (InputStream scriptStream = Objects.requireNonNull( - StartupScriptsService.class.getResourceAsStream( - "/jshell_startup/" + id + ".jsh"), + StartupScriptsService.class + .getResourceAsStream("/jshell_startup/" + id + ".jsh"), "Couldn't load script " + id)) { String script = new String(scriptStream.readAllBytes(), StandardCharsets.UTF_8); script = cleanEndLines(script); diff --git a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/Utils.java b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/Utils.java index d19457e..0c204e5 100644 --- a/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/Utils.java +++ b/JShellAPI/src/main/java/org/togetherjava/jshellapi/service/Utils.java @@ -7,8 +7,8 @@ import java.util.stream.Stream; public class Utils { - public static , X extends Exception> E nameOrElseThrow( - Class c, String name, Function exceptionFunction) throws X { + public static , X extends Exception> E nameOrElseThrow(Class c, + String name, Function exceptionFunction) throws X { return name(c, name).orElseThrow(() -> exceptionFunction.apply(name)); } @@ -16,8 +16,8 @@ public static > Optional name(Class c, String name) { return predicate(c, e -> e.name().equals(name)).findAny(); } - public static , K> Optional key( - Class c, Function keyMapper, K name) { + public static , K> Optional key(Class c, Function keyMapper, + K name) { return predicate(c, e -> keyMapper.apply(e).equals(name)).findAny(); } diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/EvalResult.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/EvalResult.java index e45e58a..264c53f 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/EvalResult.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/EvalResult.java @@ -4,4 +4,5 @@ import java.util.List; -public record EvalResult(List events, JShellEvalAbortion abortion) {} +public record EvalResult(List events, JShellEvalAbortion abortion) { +} diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortion.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortion.java index 5daddd6..6bc5267 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortion.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortion.java @@ -1,4 +1,5 @@ package org.togetherjava.jshell.wrapper; -public record JShellEvalAbortion( - String sourceCause, String remainingSource, JShellEvalAbortionCause cause) {} +public record JShellEvalAbortion(String sourceCause, String remainingSource, + JShellEvalAbortionCause cause) { +} diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortionCause.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortionCause.java index 0d3b8d3..0cb3e54 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortionCause.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellEvalAbortionCause.java @@ -4,12 +4,16 @@ public sealed interface JShellEvalAbortionCause { - record TimeoutAbortionCause() implements JShellEvalAbortionCause {} + record TimeoutAbortionCause() implements JShellEvalAbortionCause { + } - record UnhandledExceptionAbortionCause(String exceptionClass, String exceptionMessage) - implements JShellEvalAbortionCause {} + record UnhandledExceptionAbortionCause(String exceptionClass, + String exceptionMessage) implements JShellEvalAbortionCause { + } - record CompileTimeErrorAbortionCause(List errors) implements JShellEvalAbortionCause {} + record CompileTimeErrorAbortionCause(List errors) implements JShellEvalAbortionCause { + } - record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause {} + record SyntaxErrorAbortionCause() implements JShellEvalAbortionCause { + } } diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellWrapper.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellWrapper.java index 90dda35..535a052 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellWrapper.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/JShellWrapper.java @@ -45,26 +45,25 @@ public void run(Config config, InputStream in, PrintStream processOut) { private void verifyStartupEval(EvalResult result) { JShellEvalAbortion abortion = result.abortion(); - if (abortion == null) return; + if (abortion == null) + return; SnippetEvent event = result.events().get(result.events().size() - 1); // TODO Replace with switch if (abortion.cause() instanceof JShellEvalAbortionCause.TimeoutAbortionCause) { throw new RuntimeException("Timeout exceeded."); - } else if (abortion.cause() - instanceof JShellEvalAbortionCause.UnhandledExceptionAbortionCause) { - throw new RuntimeException( - "Following startup script resulted in an exception : " - + sanitize(abortion.sourceCause()), - event.exception()); - } else if (abortion.cause() - instanceof JShellEvalAbortionCause.CompileTimeErrorAbortionCause) { + } else if (abortion + .cause() instanceof JShellEvalAbortionCause.UnhandledExceptionAbortionCause) { + throw new RuntimeException("Following startup script resulted in an exception : " + + sanitize(abortion.sourceCause()), event.exception()); + } else if (abortion + .cause() instanceof JShellEvalAbortionCause.CompileTimeErrorAbortionCause) { throw new RuntimeException( "Following startup script was REJECTED : " + sanitize(abortion.sourceCause())); } else if (abortion.cause() instanceof JShellEvalAbortionCause.SyntaxErrorAbortionCause) { - throw new RuntimeException( - "Following startup script has a syntax error : " - + sanitize(abortion.sourceCause())); - } else throw new AssertionError(); + throw new RuntimeException("Following startup script has a syntax error : " + + sanitize(abortion.sourceCause())); + } else + throw new AssertionError(); } private void ok(PrintStream processOut) { @@ -78,25 +77,20 @@ private EvalResult eval(JShell shell, String code, AtomicBoolean hasStopped) { while (!code.isEmpty()) { var completion = shell.sourceCodeAnalysis().analyzeCompletion(clean(code)); if (!completion.completeness().isComplete()) { - abortion = - new JShellEvalAbortion( - code, "", new JShellEvalAbortionCause.SyntaxErrorAbortionCause()); + abortion = new JShellEvalAbortion(code, "", + new JShellEvalAbortionCause.SyntaxErrorAbortionCause()); break; } List evalEvents = shell.eval(completion.source()); JShellEvalAbortionCause abortionCause = handleEvents(shell, evalEvents, resultEvents); if (abortionCause != null) { - abortion = - new JShellEvalAbortion( - completion.source(), completion.remaining(), abortionCause); + abortion = new JShellEvalAbortion(completion.source(), completion.remaining(), + abortionCause); break; } if (hasStopped.get()) { - abortion = - new JShellEvalAbortion( - completion.source(), - completion.remaining(), - new JShellEvalAbortionCause.TimeoutAbortionCause()); + abortion = new JShellEvalAbortion(completion.source(), completion.remaining(), + new JShellEvalAbortionCause.TimeoutAbortionCause()); break; } code = completion.remaining(); @@ -104,14 +98,15 @@ private EvalResult eval(JShell shell, String code, AtomicBoolean hasStopped) { return new EvalResult(resultEvents, abortion); } - private JShellEvalAbortionCause handleEvents( - JShell shell, List evalEvents, List resultEvents) { + private JShellEvalAbortionCause handleEvents(JShell shell, List evalEvents, + List resultEvents) { for (SnippetEvent event : evalEvents) { if (event.causeSnippet() == null) { // Only keep snippet creation events resultEvents.add(event); if (event.status() == Snippet.Status.REJECTED) return createCompileErrorCause(shell, event); - if (event.exception() != null) return createExceptionCause(event); + if (event.exception() != null) + return createExceptionCause(event); } } return null; @@ -134,8 +129,8 @@ private JShellEvalAbortionCause.CompileTimeErrorAbortionCause createCompileError JShell shell, SnippetEvent event) { return new JShellEvalAbortionCause.CompileTimeErrorAbortionCause( shell.diagnostics(event.snippet()) - .map(d -> sanitize(d.getMessage(Locale.ENGLISH))) - .toList()); + .map(d -> sanitize(d.getMessage(Locale.ENGLISH))) + .toList()); } /** @@ -156,21 +151,15 @@ private JShellEvalAbortionCause.CompileTimeErrorAbortionCause createCompileError * stdout
* */ - private void eval( - Scanner processIn, - PrintStream processOut, - Config config, - JShell shell, + private void eval(Scanner processIn, PrintStream processOut, Config config, JShell shell, StringOutputStream jshellOut) { AtomicBoolean hasStopped = new AtomicBoolean(); - TimeoutWatcher watcher = - new TimeoutWatcher( - config.evalTimeoutSeconds(), new JShellEvalStop(shell, hasStopped)); + TimeoutWatcher watcher = new TimeoutWatcher(config.evalTimeoutSeconds(), + new JShellEvalStop(shell, hasStopped)); int lineCount = Integer.parseInt(processIn.nextLine()); - String code = - IntStream.range(0, lineCount) - .mapToObj(i -> processIn.nextLine()) - .collect(Collectors.joining("\n")); + String code = IntStream.range(0, lineCount) + .mapToObj(i -> processIn.nextLine()) + .collect(Collectors.joining("\n")); ok(processOut); watcher.start(); @@ -195,19 +184,20 @@ private List writeEvalResult(EvalResult result, StringOutputStream jshel // TODO replace with switch if (abortion.cause() instanceof JShellEvalAbortionCause.TimeoutAbortionCause) { outBuffer.add("TIMEOUT"); - } else if (abortion.cause() - instanceof JShellEvalAbortionCause.UnhandledExceptionAbortionCause c) { + } else if (abortion + .cause() instanceof JShellEvalAbortionCause.UnhandledExceptionAbortionCause c) { outBuffer.add("UNCAUGHT_EXCEPTION"); outBuffer.add(getExceptionFromCause(c)); - } else if (abortion.cause() - instanceof JShellEvalAbortionCause.CompileTimeErrorAbortionCause c) { + } else if (abortion + .cause() instanceof JShellEvalAbortionCause.CompileTimeErrorAbortionCause c) { outBuffer.add("COMPILE_TIME_ERROR"); outBuffer.add(String.valueOf(c.errors().size())); outBuffer.addAll(c.errors()); - } else if (abortion.cause() - instanceof JShellEvalAbortionCause.SyntaxErrorAbortionCause c) { + } else if (abortion + .cause() instanceof JShellEvalAbortionCause.SyntaxErrorAbortionCause c) { outBuffer.add("SYNTAX_ERROR"); - } else throw new AssertionError(); + } else + throw new AssertionError(); outBuffer.add(sanitize(abortion.sourceCause())); outBuffer.add(sanitize(abortion.remainingSource())); } else { @@ -231,12 +221,11 @@ private List writeEvalResult(EvalResult result, StringOutputStream jshel * */ private void writeEvalSnippetEvent(List outBuffer, SnippetEvent event) { - String status = - switch (event.status()) { - case VALID, RECOVERABLE_DEFINED, RECOVERABLE_NOT_DEFINED, REJECTED -> - event.status().name(); - default -> throw new RuntimeException("Invalid status"); - }; + String status = switch (event.status()) { + case VALID, RECOVERABLE_DEFINED, RECOVERABLE_NOT_DEFINED, REJECTED -> + event.status().name(); + default -> throw new RuntimeException("Invalid status"); + }; outBuffer.add(status); if (event.previousStatus() == Snippet.Status.NONEXISTENT) { outBuffer.add("ADDITION"); @@ -268,9 +257,9 @@ private String getExceptionFromCause( private void snippets(PrintStream processOut, JShell shell) { ok(processOut); shell.snippets() - .map(Snippet::source) - .map(JShellWrapper::sanitize) - .forEach(processOut::println); + .map(Snippet::source) + .map(JShellWrapper::sanitize) + .forEach(processOut::println); processOut.println(); } diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/StringOutputStream.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/StringOutputStream.java index 5ca0fb4..dc70ca8 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/StringOutputStream.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/StringOutputStream.java @@ -54,11 +54,10 @@ public void write(byte[] b, int off, int len) { } public Result readAll() { - if (index > bytes.length) throw new IllegalStateException(); // Should never happen - String s = - new String( - index == bytes.length ? bytes : Arrays.copyOf(bytes, index), - StandardCharsets.UTF_8); + if (index > bytes.length) + throw new IllegalStateException(); // Should never happen + String s = new String(index == bytes.length ? bytes : Arrays.copyOf(bytes, index), + StandardCharsets.UTF_8); index = 0; if (byteOverflow) { byteOverflow = false; @@ -66,7 +65,8 @@ public Result readAll() { s.charAt(s.length() - 1) == UNKNOWN_CHAR ? s.substring(0, s.length() - 1) : s, true); } - if (s.length() > maxSize) return new Result(s.substring(0, maxSize), true); + if (s.length() > maxSize) + return new Result(s.substring(0, maxSize), true); return new Result(s, false); } @@ -75,5 +75,6 @@ public void close() { bytes = null; } - public record Result(String content, boolean isOverflow) {} + public record Result(String content, boolean isOverflow) { + } } diff --git a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/TimeoutWatcher.java b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/TimeoutWatcher.java index 3f08d8e..f3a3d37 100644 --- a/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/TimeoutWatcher.java +++ b/JShellWrapper/src/main/java/org/togetherjava/jshell/wrapper/TimeoutWatcher.java @@ -5,16 +5,15 @@ public class TimeoutWatcher { private boolean timeout; public TimeoutWatcher(int timeoutSeconds, Runnable timeoutAction) { - Runnable runnable = - () -> { - try { - Thread.sleep(timeoutSeconds * 1000L); - } catch (InterruptedException e) { // Stopped - return; - } - timeout = true; - timeoutAction.run(); - }; + Runnable runnable = () -> { + try { + Thread.sleep(timeoutSeconds * 1000L); + } catch (InterruptedException e) { // Stopped + return; + } + timeout = true; + timeoutAction.run(); + }; thread = new Thread(runnable); thread.setName("Timeout Watcher"); } diff --git a/JShellWrapper/src/test/java/JShellWrapperStartupScriptTest.java b/JShellWrapper/src/test/java/JShellWrapperStartupScriptTest.java index 4825e8c..3657eaa 100644 --- a/JShellWrapper/src/test/java/JShellWrapperStartupScriptTest.java +++ b/JShellWrapper/src/test/java/JShellWrapperStartupScriptTest.java @@ -1,18 +1,17 @@ -import static org.junit.jupiter.api.Assertions.*; - import org.junit.jupiter.api.Test; + import org.togetherjava.jshell.wrapper.Config; import org.togetherjava.jshell.wrapper.JShellWrapper; import java.io.PrintStream; +import static org.junit.jupiter.api.Assertions.*; + class JShellWrapperStartupScriptTest { @Test void testDoubleSnippets() { Config config = new Config(5, 1024); - StringInputStream inputStream = - new StringInputStream( - """ + StringInputStream inputStream = new StringInputStream(""" import java.util.*; void println(Object o) { System.out.println(o); } eval 1 @@ -21,8 +20,7 @@ void testDoubleSnippets() { UnboundStringOutputStream outputStream = new UnboundStringOutputStream(); JShellWrapper jshell = new JShellWrapper(); jshell.run(config, inputStream, new PrintStream(outputStream)); - assertEquals( - """ + assertEquals(""" OK 2 OK @@ -36,7 +34,6 @@ void testDoubleSnippets() { false [a, b, c]\\n OK - """, - outputStream.readAll()); + """, outputStream.readAll()); } } diff --git a/JShellWrapper/src/test/java/JShellWrapperTest.java b/JShellWrapper/src/test/java/JShellWrapperTest.java index 86fe8e8..1d8c8f9 100644 --- a/JShellWrapper/src/test/java/JShellWrapperTest.java +++ b/JShellWrapper/src/test/java/JShellWrapperTest.java @@ -1,13 +1,14 @@ -import static org.junit.jupiter.api.Assertions.assertEquals; - import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; + import org.togetherjava.jshell.wrapper.Config; import org.togetherjava.jshell.wrapper.JShellWrapper; import java.io.InputStream; import java.io.PrintStream; +import static org.junit.jupiter.api.Assertions.assertEquals; + class JShellWrapperTest { static Config config; static JShellWrapper jshell; @@ -28,12 +29,10 @@ void evalTest(String input, String expectedOutput) { @Test void testHelloWorld() { - evalTest( - """ - eval - 1 - System.out.println("Hello world!")""", - """ + evalTest(""" + eval + 1 + System.out.println("Hello world!")""", """ OK 0 OK @@ -50,15 +49,13 @@ void testHelloWorld() { @Test void testMultilinesInput() { - evalTest( - """ - eval - 4 - for(int i = 0; i < 10; i++) { - System.out.print(i); - } - System.out.println();""", - """ + evalTest(""" + eval + 4 + for(int i = 0; i < 10; i++) { + System.out.print(i); + } + System.out.println();""", """ OK 0 OK @@ -80,12 +77,10 @@ void testMultilinesInput() { @Test void testStdoutOverflow() { - evalTest( - """ - eval - 1 - for(int i = 0; i < 1024; i++) System.out.print(0)""", - """ + evalTest(""" + eval + 1 + for(int i = 0; i < 1024; i++) System.out.print(0)""", """ OK 0 OK @@ -97,14 +92,11 @@ void testStdoutOverflow() { false - %s""" - .formatted("0".repeat(1024))); - evalTest( - """ - eval - 1 - for(int i = 0; i <= 1024; i++) System.out.print(0)""", - """ + %s""".formatted("0".repeat(1024))); + evalTest(""" + eval + 1 + for(int i = 0; i <= 1024; i++) System.out.print(0)""", """ OK 0 OK @@ -116,19 +108,16 @@ void testStdoutOverflow() { true - %s""" - .formatted("0".repeat(1024))); + %s""".formatted("0".repeat(1024))); } @Test void testModificationAndMultiplesSnippets() { - evalTest( - """ - eval - 2 - int i = 0; - int i = 2;""", - """ + evalTest(""" + eval + 2 + int i = 0; + int i = 2;""", """ OK 0 OK @@ -150,12 +139,10 @@ void testModificationAndMultiplesSnippets() { @Test void testUseId() { - evalTest( - """ - eval - 1 - System.out.println("Hello world!")""", - """ + evalTest(""" + eval + 1 + System.out.println("Hello world!")""", """ OK 0 OK @@ -172,12 +159,10 @@ void testUseId() { @Test void testTimeout() { - evalTest( - """ - eval - 1 - while(true);""", - """ + evalTest(""" + eval + 1 + while(true);""", """ OK 0 OK @@ -196,12 +181,10 @@ void testTimeout() { @Test void testUncaughtException() { // TODO other kind of exception, not in EvalException - evalTest( - """ - eval - 1 - throw new RuntimeException("Some message : fail")""", - """ + evalTest(""" + eval + 1 + throw new RuntimeException("Some message : fail")""", """ OK 0 OK @@ -221,12 +204,10 @@ void testUncaughtException() { // TODO other kind of exception, not in EvalExcep @Test void testRejected() { - evalTest( - """ - eval - 1 - print""", - """ + evalTest(""" + eval + 1 + print""", """ OK 0 OK @@ -248,12 +229,10 @@ void testRejected() { @Test void testSyntaxError() { // DEFINITELY_INCOMPLETE - evalTest( - """ - eval - 1 - print(""", - """ + evalTest(""" + eval + 1 + print(""", """ OK 0 OK @@ -264,12 +243,10 @@ void testSyntaxError() { false """); // CONSIDERED_INCOMPLETE - evalTest( - """ - eval - 1 - while(true)""", - """ + evalTest(""" + eval + 1 + while(true)""", """ OK 0 OK @@ -279,12 +256,10 @@ void testSyntaxError() { false """); - evalTest( - """ - eval - 1 - for(int i = 0; i < 10; i++)""", - """ + evalTest(""" + eval + 1 + for(int i = 0; i < 10; i++)""", """ OK 0 OK @@ -298,14 +273,12 @@ void testSyntaxError() { @Test void testRejectedAndMultiples() { - evalTest( - """ - eval - 3 - int i = 0; - print; - System.out.println(i);""", - """ + evalTest(""" + eval + 3 + int i = 0; + print; + System.out.println(i);""", """ OK 0 OK @@ -331,14 +304,12 @@ void testRejectedAndMultiples() { @Test void testMultilinesAndHardcodedNewLineInString() { - evalTest( - """ - eval - 3 - { - System.out.println("\\n"); - }""", - """ + evalTest(""" + eval + 3 + { + System.out.println("\\n"); + }""", """ OK 0 OK diff --git a/JShellWrapper/src/test/java/StringInputStream.java b/JShellWrapper/src/test/java/StringInputStream.java index 8bd679b..1293d9e 100644 --- a/JShellWrapper/src/test/java/StringInputStream.java +++ b/JShellWrapper/src/test/java/StringInputStream.java @@ -11,7 +11,8 @@ public StringInputStream(String content) { @Override public int read() { - if (i == content.length) return -1; + if (i == content.length) + return -1; return content[i++] & 0xFF; } } diff --git a/JShellWrapper/src/test/java/StringOutputStreamTest.java b/JShellWrapper/src/test/java/StringOutputStreamTest.java index f106986..8129f11 100644 --- a/JShellWrapper/src/test/java/StringOutputStreamTest.java +++ b/JShellWrapper/src/test/java/StringOutputStreamTest.java @@ -1,12 +1,13 @@ -import static org.junit.jupiter.api.Assertions.*; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import org.togetherjava.jshell.wrapper.StringOutputStream; import java.nio.charset.StandardCharsets; +import static org.junit.jupiter.api.Assertions.*; + class StringOutputStreamTest { static final String E_ACUTE = "\u00E9"; static final String SMILEY = "\uD83D\uDE0A"; @@ -83,14 +84,12 @@ void testWriteOverload() { assertResult(true, E_ACUTE.repeat(10), stream.readAll()); final String eAcuteX5AndSmileyX5 = E_ACUTE.repeat(5) + SMILEY.repeat(5); - stream.write( - eAcuteX5AndSmileyX5.getBytes(StandardCharsets.UTF_8), 2 * 3, (2 * 2) + (4 * 4)); + stream.write(eAcuteX5AndSmileyX5.getBytes(StandardCharsets.UTF_8), 2 * 3, + (2 * 2) + (4 * 4)); assertResult(false, E_ACUTE.repeat(2) + SMILEY.repeat(4), stream.readAll()); final String eAcuteX5AndSmileyX5AndA = E_ACUTE.repeat(5) + SMILEY.repeat(5) + 'a'; - stream.write( - eAcuteX5AndSmileyX5AndA.getBytes(StandardCharsets.UTF_8), - 2 * 3, + stream.write(eAcuteX5AndSmileyX5AndA.getBytes(StandardCharsets.UTF_8), 2 * 3, (2 * 2) + (4 * 4) + 1); assertResult(true, E_ACUTE.repeat(2) + SMILEY.repeat(4), stream.readAll()); } diff --git a/build.gradle b/build.gradle index c38eabb..81ebaa4 100644 --- a/build.gradle +++ b/build.gradle @@ -16,16 +16,15 @@ subprojects { spotless { java { - importOrder() - googleJavaFormat() - .aosp() - .reflowLongStrings() - .formatJavadoc(true) - .reorderImports(true) - .groupArtifact('com.google.googlejavaformat:google-java-format') - trimTrailingWhitespace() - indentWithSpaces() + targetExclude("build/**") endWithNewline() + removeUnusedImports() + // empty string '' covers all imports that aren't explicitly specified, + // we use it as catch-all for external dependencies like JDA + // '\\#` is prefix for static imports + importOrder('','org.togetherjava', 'javax', 'java', '\\#') + // TODO: pinning version because of spotless error https://github.com/diffplug/spotless/issues/1992 + eclipse("4.31").configFile("${rootProject.rootDir}/spotless.xml") } } @@ -68,4 +67,4 @@ subprojects { testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' } -} \ No newline at end of file +} diff --git a/spotless.xml b/spotless.xml new file mode 100644 index 0000000..331dc6b --- /dev/null +++ b/spotless.xml @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +