Skip to content
Snippets Groups Projects
Commit 031b871e authored by Henry Page's avatar Henry Page :speech_balloon:
Browse files

temp

parent 61923976
Branches
Tags
2 merge requests!664Version 2.1.4,!654Resolve "Cascade soft delete of core entities to queue"
Showing
with 39 additions and 295 deletions
......@@ -8,7 +8,7 @@ version = "2.1.2"
val javaVersion = JavaVersion.VERSION_17
val libradorVersion = "1.0.3-SNAPSHOT6"
val libradorVersion = "1.2.1"
val labradoorVersion = "1.3.5"
val queryDslVersion = "4.4.0"
......
......@@ -22,6 +22,7 @@ import java.util.List;
import nl.tudelft.labracore.api.AssignmentControllerApi;
import nl.tudelft.labracore.api.dto.AssignmentDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.ClusterControllerApi;
import nl.tudelft.labracore.api.dto.ClusterDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -22,6 +22,7 @@ import java.util.List;
import nl.tudelft.labracore.api.CohortControllerApi;
import nl.tudelft.labracore.api.dto.CohortDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
/*
* Queue - A Queueing system that can be used to handle labs in higher education
* Copyright (C) 2016-2021 Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.queue.cache;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import com.google.common.collect.Sets;
public abstract class CoreCacheManager<ID, DTO> {
protected final Map<ID, DTO> cache = new ConcurrentHashMap<>();
protected final Set<ID> empties = new HashSet<>();
/**
* Fetches the objects currently missing from the cache. These objects are fetched through a list of their
* IDs.
*
* @param ids The list of IDs of objects to fetch.
* @return The list of fetched objects.
*/
protected abstract List<DTO> fetch(List<ID> ids);
/**
* Gets the ID of a particular instance of the data object.
*
* @param dto The DTO data object that will be cached.
* @return The ID of the DTO object.
*/
protected abstract ID getId(DTO dto);
/**
* @return The maximum number of items to be fetched from Labracore in one go.
*/
protected int batchSize() {
return 128;
}
/**
* Used to manually check the status of the cache and invalidate the cache if necessary.
*/
protected synchronized void validateCache() {
}
/**
* Gets a list of DTOs from a list of ids. The list of ids gets broken up into already cached items and to
* be fetched items. The to be fetched are then fetched and the already cached are just fetched from
* cache.
*
* @param ids The ids to find matching DTOs for.
* @return The list of DTOs found to be matching.
*/
public List<DTO> get(List<ID> ids) {
validateCache();
List<ID> misses = ids.stream()
.filter(id -> !cache.containsKey(id) && !empties.contains(id))
.collect(Collectors.toList());
if (!misses.isEmpty()) {
forEachBatch(misses, batch -> registerImpl(new HashSet<>(batch), fetch(batch)));
}
return ids.stream()
.map(cache::get)
.collect(Collectors.toList());
}
/**
* Gets a list of DTOs from a stream of IDs. This method is mostly used as a convenience method wrapping
* {@link #get(List)}.
*
* @param ids The stream of ids to be collected and requested from the cache.
* @return The list of DTOs with the given IDs.
*/
public List<DTO> get(Stream<ID> ids) {
return get(ids.collect(Collectors.toList()));
}
/**
* Gets a single DTO from its id. If the DTO is already cached, no external lookup is done.
*
* @param id The id of the DTO to lookup.
* @return The requested DTO if it exists, otherwise nothing.
*/
public Optional<DTO> get(ID id) {
if (id == null) {
return Optional.empty();
}
return Optional.of(get(List.of(id)))
.filter(l -> !l.isEmpty())
.map(l -> l.get(0));
}
/**
* Gets the DTO with the given id or throws an exception with status code 404 if none such DTO could be
* found.
*
* @param id The id of the DTO to find.
* @return The found DTO with the given id.
*/
public DTO getOrThrow(ID id) {
var l = get(List.of(id));
if (l == null || l.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find DTO by id " + id);
}
return l.get(0);
}
/**
* Registers an entry into the cache after validating the cache first.
*
* @param dto The DTO that is to be registered.
*/
public void register(DTO dto) {
validateCache();
registerImpl(dto);
}
/**
* Registers a list of entries into the cache after validating the cache first.
*
* @param dtos The DTOs that are to be registered.
*/
public void register(List<DTO> dtos) {
validateCache();
registerImpl(dtos);
}
/**
* Implementation of the register method. Registers an entry into the cache.
*
* @param dto The DTO that is to be registered.
*/
private void registerImpl(DTO dto) {
cache.put(getId(dto), dto);
registerAdditionally(dto);
}
/**
* Implementation of the register method. Registers a list of entries into the cache.
*
* @param dtos The DTOs that are to be registered.
*/
private void registerImpl(List<DTO> dtos) {
dtos.forEach(this::registerImpl);
}
/**
* Implementation of the register method. Registers a list of entries into the cache and also registers
* the IDs that were requested but not returned as 'empty' IDs.
*
* @param requests The set of requested IDs.
* @param dtos The list of DTOs returned from the request.
*/
private void registerImpl(Set<ID> requests, List<DTO> dtos) {
dtos.forEach(this::registerImpl);
empties.addAll(Sets.difference(requests,
dtos.stream().map(this::getId).collect(Collectors.toSet())));
}
/**
* Performs additional registers in other caches if necessary.
*
* @param dto The DTO that is being registered.
*/
protected void registerAdditionally(DTO dto) {
}
/**
* Helper function for iterating over batches of items. The batch size is determined through the
* {@link #batchSize()} method in implementations of this class. Each created batch will be passed on to
* the consumer function f.
*
* @param ids The list of ids that are to be batched.
* @param f The consumer of each batch.
*/
private void forEachBatch(List<ID> ids, Consumer<List<ID>> f) {
List<ID> batch = new ArrayList<>();
for (ID id : ids) {
batch.add(id);
if (batch.size() >= batchSize()) {
f.accept(batch);
batch.clear();
}
}
if (!batch.isEmpty()) {
f.accept(batch);
}
}
}
......@@ -24,6 +24,7 @@ import nl.tudelft.labracore.api.CourseControllerApi;
import nl.tudelft.labracore.api.dto.CourseDetailsDTO;
import nl.tudelft.labracore.api.dto.CourseSummaryDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -22,6 +22,7 @@ import java.util.List;
import nl.tudelft.labracore.api.EditionControllerApi;
import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -22,6 +22,7 @@ import java.util.List;
import nl.tudelft.labracore.api.EditionCollectionControllerApi;
import nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -25,6 +25,7 @@ import lombok.Data;
import nl.tudelft.labracore.api.EditionControllerApi;
import nl.tudelft.labracore.api.dto.RolePersonDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -22,6 +22,7 @@ import java.util.List;
import nl.tudelft.labracore.api.ModuleControllerApi;
import nl.tudelft.labracore.api.dto.ModuleDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -21,6 +21,7 @@ import java.util.List;
import nl.tudelft.labracore.api.PersonControllerApi;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.librador.cache.TimedCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
......
......@@ -24,6 +24,7 @@ import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.Id;
import nl.tudelft.labracore.api.dto.RoleDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -24,6 +24,7 @@ import nl.tudelft.labracore.api.RoomControllerApi;
import nl.tudelft.labracore.api.dto.RoomDetailsDTO;
import nl.tudelft.labracore.api.dto.RoomSummaryDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -24,6 +24,7 @@ import lombok.NoArgsConstructor;
import nl.tudelft.labracore.api.SessionControllerApi;
import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
......
......@@ -30,6 +30,7 @@ import nl.tudelft.labracore.api.dto.RolePersonLayer1DTO;
import nl.tudelft.labracore.api.dto.StudentGroupDetailsDTO;
import nl.tudelft.labracore.api.dto.StudentGroupSummaryDTO;
import nl.tudelft.librador.cache.CoreCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
......
/*
* Queue - A Queueing system that can be used to handle labs in higher education
* Copyright (C) 2016-2021 Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.queue.cache;
public abstract class TimedCacheManager<ID, DTO> extends CoreCacheManager<ID, DTO> {
/**
* The default timeout of the timed cache.
*/
public static final long DEFAULT_TIMEOUT = 60000L;
/**
* The set timeout of the timed cache in milliseconds. This many milliseconds after the last cache
* invalidation, this cache is invalidated again.
*/
private final long timeout;
/**
* The last time in milliseconds since epoch (system-time) that this cache was (in)validated.
* {@link #timeout} milliseconds after this timestamp, the cache is invalidated (again).
*/
private long lastValidation = System.currentTimeMillis();
/**
* Constructs a new timed cache with the timeout set to {@link #DEFAULT_TIMEOUT}.
*/
public TimedCacheManager() {
this.timeout = DEFAULT_TIMEOUT;
}
/**
* Constructs a new timed cache with the timeout set to the given timeout.
*
* @param timeout The timeout to set internally.
*/
public TimedCacheManager(long timeout) {
this.timeout = timeout;
}
@Override
protected synchronized void validateCache() {
long now = System.currentTimeMillis();
if (lastValidation + timeout < now) {
cache.clear();
empties.clear();
lastValidation = now;
}
}
}
......@@ -81,6 +81,9 @@ public class AssignmentController {
public String createAssignment(@PathVariable Long mId,
QueueAssignmentCreateDTO dto,
Model model) {
ModuleDetailsDTO module = mCache.getRequired(mId);
dto.setModuleId(mId);
AssignmentCreateDTO create = dto.apply();
......@@ -90,7 +93,6 @@ public class AssignmentController {
aApi.addAssignment(create).block();
ModuleDetailsDTO module = mCache.getOrThrow(mId);
return "redirect:/edition/" + module.getEdition().getId() + "/modules";
}
......@@ -106,9 +108,9 @@ public class AssignmentController {
@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
public String getAssignmentRemovePage(@PathVariable Long assignmentId,
Model model) {
var assignment = aCache.getOrThrow(assignmentId);
var module = mCache.getOrThrow(assignment.getModule().getId());
var edition = eCache.getOrThrow(module.getEdition().getId());
var assignment = aCache.getRequired(assignmentId);
var module = mCache.getRequired(assignment.getModule().getId());
var edition = eCache.getRequired(module.getEdition().getId());
model.addAttribute("edition", edition);
model.addAttribute("assignment", assignment);
......@@ -126,8 +128,8 @@ public class AssignmentController {
@PostMapping("/assignment/{assignmentId}/remove")
@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
public String removeAssignment(@PathVariable Long assignmentId) {
var assignment = aCache.getOrThrow(assignmentId);
var module = mCache.getOrThrow(assignment.getModule().getId());
var assignment = aCache.getRequired(assignmentId);
var module = mCache.getRequired(assignment.getModule().getId());
aApi.deleteAssignment(assignmentId).block();
......@@ -144,8 +146,8 @@ public class AssignmentController {
*/
private String addCreateAssignmentAttributes(Long moduleId, QueueAssignmentCreateDTO dto,
Model model) {
ModuleDetailsDTO module = mCache.getOrThrow(moduleId);
EditionDetailsDTO edition = eCache.getOrThrow(module.getEdition().getId());
ModuleDetailsDTO module = mCache.getRequired(moduleId);
EditionDetailsDTO edition = eCache.getRequired(module.getEdition().getId());
model.addAttribute("edition", edition);
model.addAttribute("_module", module);
......
......@@ -95,8 +95,8 @@ public class ModuleController {
@PreAuthorize("@permissionService.canManageModule(#moduleId)")
public String getModuleRemovePage(@PathVariable Long moduleId,
Model model) {
var module = mCache.getOrThrow(moduleId);
var edition = eCache.getOrThrow(module.getEdition().getId());
var module = mCache.getRequired(moduleId);
var edition = eCache.getRequired(module.getEdition().getId());
model.addAttribute("edition", edition);
model.addAttribute("_module", module);
......@@ -113,8 +113,8 @@ public class ModuleController {
@PostMapping("/module/{moduleId}/remove")
@PreAuthorize("@permissionService.canManageModule(#moduleId)")
public String removeModule(@PathVariable Long moduleId) {
var module = mCache.getOrThrow(moduleId);
var edition = eCache.getOrThrow(module.getEdition().getId());
var module = mCache.getRequired(moduleId);
var edition = eCache.getRequired(module.getEdition().getId());
mApi.deleteModule(moduleId).block();
......
......@@ -27,6 +27,7 @@ import java.util.stream.Collectors;
import javax.transaction.Transactional;
import nl.tudelft.labracore.api.AssignmentControllerApi;
import nl.tudelft.labracore.api.PersonControllerApi;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
......@@ -74,6 +75,9 @@ public class RequestController {
@Autowired
private PersonControllerApi pApi;
@Autowired
private AssignmentControllerApi aApi;
@Autowired
private RoleDTOService rds;
......@@ -359,9 +363,10 @@ public class RequestController {
@PreAuthorize("@permissionService.canFinishRequest(#request.id)")
public String getRequestForwardView(@AuthenticatedPerson Person user,
@PathEntity LabRequest request, Model model) {
List<PersonSummaryDTO> assistants = rds
.roles(eCache.getOrThrow(
mCache.getOrThrow(aCache.getOrThrow(request.getAssignment()).getModule().getId())
.roles(eCache.getRequired(
mCache.getRequired(aCache.getRequired(request.getAssignment()).getModule().getId())
.getEdition().getId()),
Set.of(TA, HEAD_TA, TEACHER))
.stream().filter(p -> !p.getId().equals(user.getId())).toList();
......@@ -395,7 +400,7 @@ public class RequestController {
if (forwardedTo == -1L) {
rs.forwardRequestToAnyone(request, assistant, reasonForAssistant);
} else {
rs.forwardRequestToPerson(request, assistant, pCache.getOrThrow(forwardedTo), reasonForAssistant);
rs.forwardRequestToPerson(request, assistant, pCache.getRequired(forwardedTo), reasonForAssistant);
}
redirectAttributes.addFlashAttribute("message", "Request #" + request.getId() + " forwarded");
......
......@@ -72,9 +72,9 @@ public class SharedEditionController {
@GetMapping("/{id}")
@PreAuthorize("@permissionService.isAuthenticated()")
public String getSharedEdition(@PathVariable Long id, Model model) {
var collection = editionCollectionCacheManager.getOrThrow(id);
var editions = editionCacheManager.get(
collection.getEditions().stream().map(EditionSummaryDTO::getId).toList());
var collection = editionCollectionCacheManager.getRequired(id);
var editions = editionCacheManager.getAndIgnoreMissing(collection.getEditions().stream().map(EditionSummaryDTO::getId).toList());
var teachers = editions.stream().map(edition -> roleDTOService.teachers(edition))
.flatMap(List::stream).toList();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment