summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-server/src/main/java/org
diff options
context:
space:
mode:
authorvincent van beek <vincent@vlogic.nl>2026-03-26 14:02:54 +0100
committerGitHub <noreply@github.com>2026-03-26 13:02:54 +0000
commit0ffde21b0337c606e2d0ece5bd5434a930a87dcd (patch)
tree4fd071728a8da6dcf3e6fc9fe9dca5a492100d34 /opendc-web/opendc-web-server/src/main/java/org
parent8bb98c773bc982a0dab9cf9fb20d62f60a36a5d7 (diff)
Use Quarkus Quinoa for serving web UI (#391)
* refactor(web): Migrate to Quarkus 3 This commit updates the OpenDC web server to use Quarkus 3, which changes annotations to use the Jakarta namespace for annotations. * refactor(web): Configure runtime variables for web UI This commit updates the web UI to propagate runtime variables via the next-runtime-env package. Before, we would need to replace the variables in the generated sources by Next.js, next-runtime-env works by loading a JavaScript file when opening the OpenDC web UI which contains the configuration of the web app. * refactor(web): Migrate to Quarkus Quinoa This commit updates the OpenDC web server to make use of Quarkus Quinoa for serving the web UI. This allows us to deprecate the complex Quarkus extension for serving the web UI. * refactor(web): Move web UI into Quarkus web app This commit moves the web UI into the Quarkus web server module to ensure we follow Quarkus Quinoa's conventions. * refactor(web): Merge Quarkus extension into single module This commit merges the existing Quarkus extensions into a single module to prevent build complexity. * refactor(web): Migrate web proto to Java This commit migrates the web protocol to Java and removes the dependency on Jandex Gradle. * refactor(web): Migrate to Quarkus 3 This commit updates the OpenDC web server to use Quarkus 3, which changes annotations to use the Jakarta namespace for annotations. * enable DB schema migration on DEV server * webui is not needed anymore * remove MAINTAINERS is depricated * fix quarkus.quinoa properties * revert properties change, install npm in docker image to allow building the frontend * pin postgres version, this is a best practice. Fix some properties the old ones are depricated. Added properties for local testing * fix build error * :opendc-web:opendc-web-proto:spotlessApply * fix database schema --------- Co-authored-by: Fabian Mastenbroek <mail.fabianm@gmail.com>
Diffstat (limited to 'opendc-web/opendc-web-server/src/main/java/org')
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java11
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java1
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java4
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java38
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java18
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java5
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java46
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java2
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java2
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java14
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java2
-rw-r--r--opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java4
12 files changed, 62 insertions, 85 deletions
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java
index a0ac390f..ef342e5f 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java
@@ -28,6 +28,17 @@ import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.panache.common.Parameters;
import jakarta.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.ForeignKey;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.NamedQueries;
+import jakarta.persistence.NamedQuery;
+import jakarta.persistence.Table;
import java.time.Instant;
import java.util.Map;
import org.hibernate.annotations.Type;
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java
index c2695192..80031c0a 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java
@@ -51,6 +51,7 @@ import org.opendc.web.proto.Targets;
*/
@Entity
@Table(
+ name = "portfolios",
uniqueConstraints = {
@UniqueConstraint(
name = "uk_portfolios_number",
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java
index f4e5305d..ca032e21 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java
@@ -44,7 +44,7 @@ import java.util.Set;
* A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users.
*/
@Entity
-@Table
+@Table(name = "projects")
@NamedQueries({
@NamedQuery(
name = "Project.findByUser",
@@ -52,7 +52,7 @@ import java.util.Set;
"""
SELECT a
FROM ProjectAuthorization a
- WHERE a.key.userName = :userName
+ WHERE a.key.userId = :userId
"""),
@NamedQuery(
name = "Project.allocatePortfolio",
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java
index 3776ae12..ad94ad29 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java
@@ -47,7 +47,7 @@ import org.opendc.web.proto.user.ProjectRole;
* An authorization for some user to participate in a project.
*/
@Entity
-@Table
+@Table(name = "project_authorizations")
@NamedQueries({
@NamedQuery(
name = "ProjectAuthorization.findByUser",
@@ -55,7 +55,7 @@ import org.opendc.web.proto.user.ProjectRole;
"""
SELECT a
FROM ProjectAuthorization a
- WHERE a.key.userName = :userName
+ WHERE a.key.userId = :userId
"""),
})
public class ProjectAuthorization extends PanacheEntityBase {
@@ -88,8 +88,8 @@ public class ProjectAuthorization extends PanacheEntityBase {
/**
* Construct a {@link ProjectAuthorization} object.
*/
- public ProjectAuthorization(Project project, String userName, ProjectRole role) {
- this.key = new ProjectAuthorization.Key(project.id, userName);
+ public ProjectAuthorization(Project project, String userId, ProjectRole role) {
+ this.key = new ProjectAuthorization.Key(project.id, userId);
this.project = project;
this.role = role;
}
@@ -100,25 +100,25 @@ public class ProjectAuthorization extends PanacheEntityBase {
protected ProjectAuthorization() {}
/**
- * List all projects for the user with the specified <code>userName</code>.
+ * List all projects for the user with the specified <code>userId</code>.
*
- * @param userName The identifier of the user that is requesting the list of projects.
+ * @param userId The identifier of the user that is requesting the list of projects.
* @return A query returning projects that the user has received authorization for.
*/
- public static PanacheQuery<ProjectAuthorization> findByUser(String userName) {
- return find("#ProjectAuthorization.findByUser", Parameters.with("userName", userName));
+ public static PanacheQuery<ProjectAuthorization> findByUser(String userId) {
+ return find("#ProjectAuthorization.findByUser", Parameters.with("userId", userId));
}
/**
- * Find the project with <code>id</code> for the user with the specified <code>userName</code>.
+ * Find the project with <code>id</code> for the user with the specified <code>userId</code>.
*
- * @param userName The identifier of the user that is requesting the list of projects.
- * @param project_id The unique identifier of the project.
+ * @param userId The identifier of the user that is requesting the list of projects.
+ * @param id The unique identifier of the project.
* @return The project with the specified identifier or <code>null</code> if it does not exist or is not accessible
* to the user with the specified identifier.
*/
- public static ProjectAuthorization findByUser(String userName, long project_id) {
- return findById(new ProjectAuthorization.Key(project_id, userName));
+ public static ProjectAuthorization findByUser(String userId, long id) {
+ return findById(new ProjectAuthorization.Key(id, userId));
}
/**
@@ -146,12 +146,12 @@ public class ProjectAuthorization extends PanacheEntityBase {
@Column(name = "project_id", nullable = false)
public long projectId;
- @Column(name = "user_name", nullable = false)
- public String userName;
+ @Column(name = "user_id", nullable = false)
+ public String userId;
- public Key(long projectId, String userName) {
+ public Key(long projectId, String userId) {
this.projectId = projectId;
- this.userName = userName;
+ this.userId = userId;
}
protected Key() {}
@@ -161,12 +161,12 @@ public class ProjectAuthorization extends PanacheEntityBase {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
- return projectId == key.projectId && userName.equals(key.userName);
+ return projectId == key.projectId && userId.equals(key.userId);
}
@Override
public int hashCode() {
- return Objects.hash(projectId, userName);
+ return Objects.hash(projectId, userId);
}
}
}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java
index c79ef5bb..0224ae43 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java
@@ -27,6 +27,20 @@ import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.panache.common.Parameters;
import jakarta.persistence.*;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embedded;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.ForeignKey;
+import jakarta.persistence.Index;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.NamedQueries;
+import jakarta.persistence.NamedQuery;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+import jakarta.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.Type;
@@ -37,6 +51,7 @@ import org.opendc.web.proto.OperationalPhenomena;
*/
@Entity
@Table(
+ name = "scenarios",
uniqueConstraints = {
@UniqueConstraint(
name = "uk_scenarios_number",
@@ -109,13 +124,10 @@ public class Scenario extends PanacheEntityBase {
/**
* Operational phenomena activated in the scenario.
- * @Column(columnDefinition = "jsonb", nullable = false, updatable = false)
- * @Type(JsonType.class)
*/
@Column(columnDefinition = "jsonb", nullable = false, updatable = false)
@Type(JsonType.class)
public OperationalPhenomena phenomena;
-
/**
* The name of the VM scheduler used in the scenario.
*/
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java
index 8a4e2ae2..ff8b4416 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java
@@ -41,13 +41,14 @@ import jakarta.persistence.UniqueConstraint;
import java.time.Instant;
import java.util.List;
import org.hibernate.annotations.Type;
-import org.opendc.web.proto.Room;
+import org.opendc.web.proto.topology.Room;
/**
* A datacenter design in OpenDC.
*/
@Entity
@Table(
+ name = "topologies",
uniqueConstraints = {
@UniqueConstraint(
name = "uk_topologies_number",
@@ -103,8 +104,6 @@ public class Topology extends PanacheEntityBase {
/**
* Datacenter design in JSON
- * @Column(columnDefinition = "jsonb", nullable = false)
- * @Type(JsonType.class)
*/
@Column(columnDefinition = "jsonb", nullable = false)
@Type(JsonType.class)
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java
deleted file mode 100644
index 345acdfe..00000000
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2023 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.server.rest.error;
-
-import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.ext.ExceptionMapper;
-import jakarta.ws.rs.ext.Provider;
-import org.opendc.web.proto.ProtocolError;
-
-/**
- * An [ExceptionMapper] for [MissingKotlinParameterException] thrown by Jackson.
- */
-@Provider
-public final class MissingKotlinParameterExceptionMapper implements ExceptionMapper<MissingKotlinParameterException> {
- @Override
- public Response toResponse(MissingKotlinParameterException exception) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(new ProtocolError(
- Response.Status.BAD_REQUEST.getStatusCode(),
- "Field " + exception.getParameter().getName() + " is missing from body."))
- .type(MediaType.APPLICATION_JSON)
- .build();
- }
-}
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java
index 4dde8654..2b774082 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java
@@ -98,7 +98,7 @@ public final class JobResource {
}
try {
- jobService.updateJob(job, update.getState(), update.getRuntime(), update.getResults());
+ jobService.updateJob(job, update.state(), update.runtime(), update.results());
} catch (IllegalArgumentException e) {
throw new WebApplicationException(e, 400);
} catch (IllegalStateException e) {
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java
index 2a3a40f4..e4d5362c 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java
@@ -100,7 +100,7 @@ public final class PortfolioResource {
var project = auth.project;
int number = project.allocatePortfolio(now);
- Portfolio portfolio = new Portfolio(project, number, request.getName(), request.getTargets());
+ Portfolio portfolio = new Portfolio(project, number, request.name(), request.targets());
project.portfolios.add(portfolio);
portfolio.persist();
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java
index 789808c8..ea87a7ad 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java
@@ -118,12 +118,12 @@ public final class PortfolioScenarioResource {
throw new WebApplicationException("Portfolio not found", 404);
}
- Topology topology = Topology.findByProject(projectId, (int) request.getTopology());
+ Topology topology = Topology.findByProject(projectId, (int) request.topology());
if (topology == null) {
throw new WebApplicationException("Referred topology does not exist", 400);
}
- Trace trace = Trace.findById(request.getWorkload().getTrace());
+ Trace trace = Trace.findById(request.workload().trace());
if (trace == null) {
throw new WebApplicationException("Referred trace does not exist", 400);
}
@@ -136,14 +136,14 @@ public final class PortfolioScenarioResource {
project,
portfolio,
number,
- request.getName(),
- new Workload(trace, request.getWorkload().getSamplingFraction()),
+ request.name(),
+ new Workload(trace, request.workload().samplingFraction()),
topology,
- request.getPhenomena(),
- request.getSchedulerName());
+ request.phenomena(),
+ request.schedulerName());
scenario.persist();
- Job job = new Job(scenario, userId, now, portfolio.targets.getRepeats());
+ Job job = new Job(scenario, userId, now, portfolio.targets.repeats());
job.persist();
// Fail the job if there is not enough budget for the simulation
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java
index ae1c959e..40ebc666 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java
@@ -79,7 +79,7 @@ public final class ProjectResource {
@Consumes("application/json")
public org.opendc.web.proto.user.Project create(@Valid org.opendc.web.proto.user.Project.Create request) {
Instant now = Instant.now();
- Project entity = new Project(request.getName(), now);
+ Project entity = new Project(request.name(), now);
entity.persist();
ProjectAuthorization authorization =
diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java
index b8c542d3..25819e32 100644
--- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java
+++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java
@@ -104,7 +104,7 @@ public final class TopologyResource {
Project project = auth.project;
int number = project.allocateTopology(now);
- Topology topology = new Topology(project, number, request.getName(), now, request.getRooms());
+ Topology topology = new Topology(project, number, request.name(), now, request.rooms());
project.topologies.add(topology);
topology.persist();
@@ -164,7 +164,7 @@ public final class TopologyResource {
}
entity.updatedAt = Instant.now();
- entity.rooms = request.getRooms();
+ entity.rooms = request.rooms();
return UserProtocol.toDto(entity, auth);
}