Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions src/main/java/edu/kit/provideq/toolbox/ProcessRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ public ProcessResult run(ProblemType problemType, long solutionId, String proble
try {
Process process = processBuilder.start();

processOutput = readStream(process.inputReader()) + readStream(process.errorReader());
processOutput = resourceProvider.readStream(process.inputReader())
+ resourceProvider.readStream(process.errorReader());

processExitCode = process.waitFor();
} catch (IOException | InterruptedException e) {
Expand Down Expand Up @@ -221,18 +222,6 @@ private void addCommand(String command) {
processBuilder.command(existingCommands);
}

private String readStream(BufferedReader reader) throws IOException {
var inputBuilder = new StringBuilder();
var line = reader.readLine();
while (line != null) {
inputBuilder.append(line).append('\n');
line = reader.readLine();
}
reader.close();

return inputBuilder.toString();
}

protected static ProcessBuilder createGenericProcessBuilder(
String directory,
String executableName,
Expand Down
76 changes: 72 additions & 4 deletions src/main/java/edu/kit/provideq/toolbox/ResourceProvider.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package edu.kit.provideq.toolbox;

import edu.kit.provideq.toolbox.meta.ProblemType;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -60,6 +65,72 @@ public File getProblemDirectory(ProblemType problemType, long solutionId) throws
return dir.toFile();
}

public List<String> getExampleProblems(String examplesDirectoryPath) throws IOException {
// Reading the directory yields all names of the files in the directory, one per line
return readResourceString(examplesDirectoryPath)
.lines()
.map(file -> {
try {
return readResourceString(examplesDirectoryPath + "/" + file);
} catch (Exception e) {
return null;
}
})
.toList();
}

/**
* Reads the input stream of a {@link BufferedReader} into a string.
*
* @param reader reader to read from
* @return full string of the input stream
* @throws IOException when the input stream couldn't be read
*/
public String readStream(BufferedReader reader) throws IOException {
var inputBuilder = new StringBuilder();

// Process first line manually to avoid adding a newline at the beginning
var line = reader.readLine();
if (line != null) {
inputBuilder.append(line);
}

line = reader.readLine();
while (line != null) {
inputBuilder.append('\n').append(line);
line = reader.readLine();
}
reader.close();

return inputBuilder.toString();
}

/**
* Reads the input stream of an {@link InputStream} into a string.
*
* @param stream stream to read from
* @return full string of the input stream
* @throws IOException when the input stream couldn't be read
*/
public String readStream(InputStream stream) throws IOException {
return readStream(new BufferedReader(new InputStreamReader(stream)));
}

/**
* Reads the resource at the specified resource path into a string.
*
* @param resourcePath path that points to the requested resource
* @return full string of the data inside the resource
* @throws IOException when the resource is not available or couldn't be read
*/
public String readResourceString(String resourcePath) throws IOException {
Resource resource = resourceLoader.getResource("classpath:" + resourcePath);

try (var reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
return readStream(reader);
}
}

/**
* Returns the resource at the specified resource path.
*
Expand All @@ -68,11 +139,8 @@ public File getProblemDirectory(ProblemType problemType, long solutionId) throws
* @throws IOException when the resource is not available
*/
public File getResource(String resourcePath) throws IOException {
return getRootFile(resourcePath);

/* removed as long as we're not using resources directly
Resource resource = resourceLoader.getResource("classpath:" + resourcePath);
return resource.getFile();*/
return resource.getFile();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.kit.provideq.toolbox.MetaSolverProvider;
import edu.kit.provideq.toolbox.meta.MetaSolver;
import edu.kit.provideq.toolbox.meta.ProblemType;
import edu.kit.provideq.toolbox.meta.setting.MetaSolverSetting;
import org.springdoc.core.fn.builders.operation.Builder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -51,18 +54,43 @@ private RouterFunction<ServerResponse> defineMetaSolverSettingsRouteForMetaSolve
return route().GET(
getRouteForProblemType(problemType),
req -> handleMetaSolverSettingsRouteForMetaSolver(metaSolver),
ops -> ops
ops -> handleMetaSolverSettingsRouteDocumentation(ops, metaSolver)
).build();
}

private void handleMetaSolverSettingsRouteDocumentation(
Builder ops, MetaSolver<?, ?, ?> metaSolver) {
var problemType = metaSolver.getProblemType();
ops
.operationId(getRouteForProblemType(problemType))
.tag(problemType.getId())
.response(responseBuilder()
.responseCode(String.valueOf(HttpStatus.OK.value()))
.content(contentBuilder()
.description(("Returns the selection of settings available for of the "
+ problemType.getId() + " meta-solver. Settings can be used to configure"
+ " what the meat solver considers to choose the best solver."))
.response(getResponseOk(metaSolver));
}
Comment thread
schweikart marked this conversation as resolved.

private static org.springdoc.core.fn.builders.apiresponse.Builder getResponseOk(
MetaSolver<?, ?, ?> metaSolver) {
String example;
try {
example = new ObjectMapper().writeValueAsString(metaSolver.getSettings());
} catch (JsonProcessingException e) {
throw new RuntimeException("example could not be parsed", e);
}

return responseBuilder()
.responseCode(String.valueOf(HttpStatus.OK.value()))
.content(contentBuilder()
.mediaType(APPLICATION_JSON_VALUE)
.example(org.springdoc.core.fn.builders.exampleobject.Builder
.exampleOjectBuilder()
.name(metaSolver.getProblemType().getId())
.value(example))
.array(arraySchemaBuilder().schema(
schemaBuilder().implementation(MetaSolverSetting.class)))
)
)
).build();
schemaBuilder().implementation(MetaSolverSetting.class))
)
);
}

private Mono<ServerResponse> handleMetaSolverSettingsRouteForMetaSolver(
Expand Down
152 changes: 135 additions & 17 deletions src/main/java/edu/kit/provideq/toolbox/api/SolveRouter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.kit.provideq.toolbox.api;

import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
import static org.springdoc.core.fn.builders.arrayschema.Builder.arraySchemaBuilder;
import static org.springdoc.core.fn.builders.content.Builder.contentBuilder;
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
Expand All @@ -11,12 +12,19 @@
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.kit.provideq.toolbox.MetaSolverProvider;
import edu.kit.provideq.toolbox.Solution;
import edu.kit.provideq.toolbox.SolveRequest;
import edu.kit.provideq.toolbox.meta.MetaSolver;
import edu.kit.provideq.toolbox.meta.ProblemSolver;
import edu.kit.provideq.toolbox.meta.ProblemType;
import edu.kit.provideq.toolbox.meta.SubRoutineDefinition;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.springdoc.core.fn.builders.operation.Builder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
Expand Down Expand Up @@ -57,26 +65,11 @@ RouterFunction<ServerResponse> getSolveRoutes() {
}

private RouterFunction<ServerResponse> defineRouteForMetaSolver(MetaSolver<?, ?, ?> metaSolver) {
var problemType = metaSolver.getProblemType();
return route().POST(
getSolveRouteForProblemType(problemType),
getSolveRouteForProblemType(metaSolver.getProblemType()),
accept(APPLICATION_JSON),
req -> handleRouteForMetaSolver(metaSolver, req),
ops -> ops
.operationId(getSolveRouteForProblemType(problemType))
.tag(problemType.getId())
.requestBody(requestBodyBuilder()
.content(contentBuilder()
.schema(schemaBuilder().implementation(
metaSolver.getProblemType().getRequestType()))
.mediaType(APPLICATION_JSON_VALUE)
)
.required(true)
)
.response(responseBuilder()
.responseCode(String.valueOf(HttpStatus.OK.value()))
.implementation(Solution.class)
)
ops -> handleRouteDocumentation(metaSolver, ops)
).build();
}

Expand Down Expand Up @@ -141,6 +134,131 @@ private Mono<ServerResponse> handleSolutionRouteForMetaSolver(MetaSolver<?, ?, ?
});
}

private void handleRouteDocumentation(MetaSolver<?, ?, ?> metaSolver, Builder ops) {
var problemType = metaSolver.getProblemType();
ops
.operationId(getSolveRouteForProblemType(problemType))
.tag(problemType.getId())
.description("Solves a " + problemType.getId() + " problem. To solve the problem, "
+ "either the meta-solver will choose the best available solver,"
+ "or a specific solver selected in the request will be used.")
.requestBody(requestBodyBuilder()
.content(getRequestContent(metaSolver))
.required(true))
.response(getResponseOk(metaSolver));
}

private static org.springdoc.core.fn.builders.apiresponse.Builder getResponseOk(
MetaSolver<?, ?, ?> metaSolver) {
return responseBuilder()
.responseCode(String.valueOf(HttpStatus.OK.value()))
.content(contentBuilder()
.mediaType(APPLICATION_JSON_VALUE)
.example(getExampleSolved(metaSolver))
.example(getExampleInvalid(metaSolver))
.array(arraySchemaBuilder().schema(
schemaBuilder().implementation(Solution.class))));
}

private static org.springdoc.core.fn.builders.exampleobject.Builder getExampleSolved(
MetaSolver<?, ?, ?> metaSolver) {
return getExampleOk(
"Solved",
metaSolver,
solution -> {
solution.setSolutionData("Solution data to solve the problem");
Comment thread
schweikart marked this conversation as resolved.
solution.complete();
});
}

private static org.springdoc.core.fn.builders.exampleobject.Builder getExampleInvalid(
MetaSolver<?, ?, ?> metaSolver) {
return getExampleOk(
"Error",
metaSolver,
solution -> {
solution.setDebugData("Some error occurred");
Comment thread
schweikart marked this conversation as resolved.
solution.abort();
});
}

private static org.springdoc.core.fn.builders.exampleobject.Builder getExampleOk(
String exampleName,
MetaSolver<?, ?, ?> metaSolver,
Consumer<Solution<String>> solutionModifier) {
// Prepare a solved solution with some example data
var solvedSolution = new Solution<String>(42);
solvedSolution.setExecutionMilliseconds(42);
metaSolver.getAllSolvers().stream()
.findFirst()
.ifPresent(solver -> solvedSolution.setSolverName(solver.getName()));
solutionModifier.accept(solvedSolution);

// Convert the solution to a string
String solvedSolutionString;
try {
solvedSolutionString = new ObjectMapper().writeValueAsString(solvedSolution);
} catch (JsonProcessingException e) {
throw new RuntimeException("example could not be parsed", e);
}

// Build the example
return org.springdoc.core.fn.builders.exampleobject.Builder
.exampleOjectBuilder()
.name(exampleName)
.description("The problem was solved successfully.")
.value(solvedSolutionString);
}

private org.springdoc.core.fn.builders.content.Builder getRequestContent(
MetaSolver<?, ?, ?> metaSolver) {
Object content = metaSolver.getExampleProblems().stream()
.findFirst()
.orElseThrow(() -> new RuntimeException("no example available"));

var request = new SolveRequest<>();
request.requestContent = content;
request.requestedMetaSolverSettings = metaSolver.getSettings();

metaSolver.getAllSolvers().stream()
.findFirst()
.ifPresentOrElse(solver -> {
request.requestedSolverId = solver.getId();
request.requestedSubSolveRequests = solver.getSubRoutines().stream()
.collect(Collectors.toMap(
SubRoutineDefinition::type,
subRoutine -> {
var subSolveRequest = new SolveRequest<>();

subSolveRequest.requestedSolverId = metaSolverProvider
.getMetaSolver(subRoutine.type())
.getAllSolvers().stream()
.findFirst()
.map(ProblemSolver::getId)
.orElse("");
return subSolveRequest;
}));
}, () -> {
throw new RuntimeException("no solver found");
});

String requestString;
try {
requestString = new ObjectMapper().writeValueAsString(request);
} catch (JsonProcessingException exception) {
throw new RuntimeException("no example available", exception);
}

var problemType = metaSolver.getProblemType();
return contentBuilder()
.example(org.springdoc.core.fn.builders.exampleobject.Builder.exampleOjectBuilder()
.name(problemType.getId())
.value(requestString))
.schema(schemaBuilder().implementation(
problemType.getRequestType()))
.mediaType(APPLICATION_JSON_VALUE);
}

private String getSolveRouteForProblemType(ProblemType type) {
return "/solve/" + type.getId();
}
Expand Down
Loading