Skip to content

Commit

Permalink
Merge branch 'main' into feature/jdk17
Browse files Browse the repository at this point in the history
  • Loading branch information
dfuchss authored Jul 14, 2024
2 parents 34a44c5 + 02e81de commit e27ee7d
Show file tree
Hide file tree
Showing 148 changed files with 985 additions and 603 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
with:
body: 'Minor Release (mostly for the automatic download of the jar file)'
files: autograder-cmd/target/autograder-cmd.jar
files: autograder-cmd/target/autograder-cmd.jar
42 changes: 42 additions & 0 deletions autograder-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>
<groupId>de.firemage.autograder</groupId>
<artifactId>autograder-parent</artifactId>
<version>0.5.41</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>autograder-api</artifactId>
<name>autograder-api</name>
<description>Frontend API for the autograder</description>
<url>https://github.com/Feuermagier/autograder/autograder-api</url>

<dependencies>
<dependency>
<groupId>net.xyzsd.fluent</groupId>
<artifactId>fluent-base</artifactId>
<version>${fluent.version}</version>
</dependency>

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>

<!-- Config -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.firemage.autograder.api;

import java.nio.file.Path;

public interface AbstractCodePosition {
Path path();
int startLine();

int endLine();

int startColumn();

int endColumn();
String readSourceFile();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package de.firemage.autograder.api;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;

public interface AbstractLinter {
List<? extends AbstractProblem> checkFile(Path file, JavaVersion version, CheckConfiguration checkConfiguration, Consumer<Translatable> statusConsumer) throws LinterException, IOException;
String translateMessage(Translatable translatable);

static Builder builder(Locale locale) {
return new Builder(locale);
}

class Builder {
private final Locale locale;
private AbstractTempLocation tempLocation;
private int threads;
private ClassLoader classLoader;
private int maxProblemsPerCheck = -1;

private Builder(Locale locale) {
this.locale = locale;
}

public Builder tempLocation(AbstractTempLocation tempLocation) {
this.tempLocation = tempLocation;
return this;
}

public AbstractTempLocation getTempLocation() {
return tempLocation;
}

public Builder threads(int threads) {
this.threads = threads;
return this;
}

public int getThreads() {
return threads;
}

public Builder maxProblemsPerCheck(int maxProblemsPerCheck) {
this.maxProblemsPerCheck = maxProblemsPerCheck;
return this;
}

public int getMaxProblemsPerCheck() {
return maxProblemsPerCheck;
}

public Builder classLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}

public ClassLoader getClassLoader() {
return classLoader;
}

public Locale getLocale() {
return locale;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.firemage.autograder.api;

public interface AbstractProblem {
String getCheckName();
Translatable getLinterName();
Translatable getExplanation();
String getDisplayLocation();
AbstractCodePosition getPosition();
String getType();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package de.firemage.autograder.api;

public interface AbstractProblemType {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.firemage.autograder.api;

import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Path;

public interface AbstractTempLocation extends Closeable, Serializable {
AbstractTempLocation createTempDirectory(String prefix) throws IOException;
Path createTempFile(String name) throws IOException;
Path toPath();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

import java.io.IOException;
import java.nio.file.Files;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import de.firemage.autograder.api.loader.ImplementationBinder;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public record CheckConfiguration(List<ProblemType> problemsToReport, List<String> excludedClasses) {
public record CheckConfiguration(List<? extends AbstractProblemType> problemsToReport, List<String> excludedClasses) {
public static CheckConfiguration empty() {
return new CheckConfiguration(List.of(), List.of());
}
Expand All @@ -21,12 +23,19 @@ public static CheckConfiguration fromConfigString(String configString) throws IO
if (!configString.contains("problemsToReport") && configString.startsWith("[")) {
configString = "problemsToReport: " + configString;
}
var config = new ObjectMapper(new YAMLFactory()).readValue(configString, CheckConfiguration.class);

// Tell Jackson how to instantiate AbstractProblemType
var coreModule = new SimpleModule("autograder-core");
coreModule.addAbstractTypeMapping(AbstractProblemType.class, new ImplementationBinder<>(AbstractProblemType.class).findImplementation());

var mapper = new ObjectMapper(new YAMLFactory());
mapper.registerModule(coreModule);
var config = mapper.readValue(configString, CheckConfiguration.class);
config.validate();
return config;
}

public static CheckConfiguration fromProblemTypes(List<ProblemType> problemsToReport) {
public static CheckConfiguration fromProblemTypes(List<? extends AbstractProblemType> problemsToReport) {
return new CheckConfiguration(problemsToReport, List.of());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core.compiler;
package de.firemage.autograder.api;

import java.util.Arrays;
import java.util.Comparator;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

public class LinterConfigurationException extends LinterException {
public LinterConfigurationException(String message) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

public class LinterException extends Exception {
public LinterException() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.firemage.autograder.api;

import java.nio.file.Path;

public interface PathLike {
Path toPath();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.firemage.autograder.core;
package de.firemage.autograder.api;

import fluent.bundle.FluentBundle;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package de.firemage.autograder.api.loader;

import de.firemage.autograder.api.AbstractLinter;
import de.firemage.autograder.api.AbstractProblemType;
import de.firemage.autograder.api.AbstractTempLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.nio.file.Files;

public class AutograderLoader {
private static final String AUTOGRADER_DOWNLOAD_URL = "https://github.com/Feuermagier/autograder/releases/latest/download/autograder-cmd.jar";
private static final String AUTOGRADER_RELEASE_PATH = "https://github.com/Feuermagier/autograder/releases/latest";

private static final Logger LOG = LoggerFactory.getLogger(AutograderLoader.class);

private static URLClassLoader autograderClassLoader = null;
private static String currentTag = null;
private static Path downloadedAutograderPath = null;

public static void loadFromFile(Path autograderPath) throws IOException {
loadAutograderIntoProcess(autograderPath);
}

public static void loadFromGithubWithExtraChecks() throws IOException {
String tag = getAutograderVersionTag();
if (downloadedAutograderPath == null || !downloadedAutograderPath.getFileName().startsWith(tag)) {
downloadAutograderRelease(tag);
}
currentTag = tag;
loadAutograderIntoProcess(downloadedAutograderPath);
}

public static boolean isCurrentVersionLoaded() throws IOException {
if (downloadedAutograderPath == null) {
return true;
}

return getAutograderVersionTag().equals(currentTag);
}

public static boolean isAutograderLoaded() {
return autograderClassLoader != null;
}

public static AbstractLinter instantiateLinter(AbstractLinter.Builder builder) {
return new ImplementationBinder<>(AbstractLinter.class)
.param(AbstractLinter.Builder.class, builder)
.classLoader(autograderClassLoader)
.instantiate();
}

public static AbstractTempLocation instantiateTempLocation(Path path) {
return new ImplementationBinder<>(AbstractTempLocation.class)
.param(Path.class, path)
.classLoader(autograderClassLoader)
.instantiate();
}

public static AbstractTempLocation instantiateTempLocation() {
return new ImplementationBinder<>(AbstractTempLocation.class)
.classLoader(autograderClassLoader)
.instantiate();
}

public static AbstractProblemType convertProblemType(String problemType) {
return new ImplementationBinder<>(AbstractProblemType.class)
.param(String.class, problemType)
.classLoader(autograderClassLoader)
.callStatic("fromString", AbstractProblemType.class);
}

private static String getAutograderVersionTag() throws IOException {
URLConnection connection = new URL(AUTOGRADER_RELEASE_PATH).openConnection();
connection.connect();
// Open stream to force redirect to the latest release
try (var inputStream = connection.getInputStream()) {
String[] components = connection.getURL().getFile().split("/");
return components[components.length - 1];
}
}

private static void downloadAutograderRelease(String tag) throws IOException {
Path targetPath = Files.createTempFile(tag + "_autograder_jar", ".jar");
LOG.info("Downloading autograder JAR with version/tag {} to {}", tag, targetPath.toAbsolutePath());
Files.deleteIfExists(targetPath);
Files.createFile(targetPath);
URL url = new URL(AUTOGRADER_DOWNLOAD_URL);
ReadableByteChannel channel = Channels.newChannel(url.openStream());
try (var outStream = new FileOutputStream(targetPath.toFile())) {
outStream.getChannel().transferFrom(channel, 0, Long.MAX_VALUE);
}
downloadedAutograderPath = targetPath;
}

private static void loadAutograderIntoProcess(Path jar) throws IOException {
if (autograderClassLoader != null) {
throw new IllegalStateException("Autograder already loaded. Restart the process to load a new version.");
}

if (!Files.exists(jar)) {
throw new IOException("Autograder JAR not found at " + jar.toAbsolutePath());
}

LOG.info("Loading autograder JAR from {}", jar.toAbsolutePath());
autograderClassLoader = new URLClassLoader(new URL[]{jar.toUri().toURL()}, AutograderLoader.class.getClassLoader());
}
}
Loading

0 comments on commit e27ee7d

Please sign in to comment.