package com.github.retro_game.retro_game.cache; import com.github.retro_game.retro_game.dto.BodyInfoDto; import com.github.retro_game.retro_game.repository.BodyRepository; import com.github.retro_game.retro_game.service.impl.Converter; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.UncheckedExecutionException; import org.springframework.stereotype.Component; import javax.annotation.ParametersAreNonnullByDefault; import javax.persistence.EntityNotFoundException; import java.util.Optional; import java.util.concurrent.ExecutionException; @Component public class BodyInfoCache { private final static int MAX_SIZE = 4096; // Guava provides a thread-safe implementation, no synchronization is required. private final LoadingCache<Long, BodyInfoDto> cache; public BodyInfoCache(BodyRepository bodyRepository) { cache = CacheBuilder.newBuilder() .maximumSize(MAX_SIZE) .build(new CacheLoader<>() { @Override @ParametersAreNonnullByDefault public BodyInfoDto load(Long bodyId) { var body = bodyRepository.getOne(bodyId); // TODO: This will load the user, which is unnecessary. We could probably keep only the id of the // owner in the body entity. var userId = body.getUser().getId(); var coords = Converter.convert(body.getCoordinates()); return new BodyInfoDto(bodyId, userId, body.getName(), coords); } } ); } public BodyInfoDto get(long bodyId) { return cache.getUnchecked(bodyId); } public Optional<BodyInfoDto> find(long bodyId) { try { return Optional.of(get(bodyId)); } catch (UncheckedExecutionException e) { if (e.getCause() instanceof EntityNotFoundException) { return Optional.empty(); } throw e; } } public ImmutableMap<Long, BodyInfoDto> getAll(Iterable<Long> bodiesIds) { try { return cache.getAll(bodiesIds); } catch (ExecutionException e) { throw new UncheckedExecutionException(e); } } public void evict(long bodyId) { cache.invalidate(bodyId); } }