use of net.robinfriedli.exec.modes.MutexSyncMode in project aiode by robinfriedli.
the class Artist method getOrCreateArtist.
/**
* Get the existing artist with the provided or create a new artist. This method runs synchronised on the artist id
* to avoid unique constraint violations during concurrent creation.
*
* @param artist the spotify artist
* @param session the hibernate session
*/
public static Artist getOrCreateArtist(ArtistSimplified artist, Session session) {
QueryBuilderFactory queryBuilderFactory = Aiode.get().getQueryBuilderFactory();
Mode mode = Mode.create().with(new MutexSyncMode<>(artist.getId(), ARTIST_SYNC));
return HibernateInvoker.create(session).invokeFunction(mode, currentSession -> {
Optional<Artist> existingArtist = queryBuilderFactory.find(Artist.class).where((cb, root) -> cb.equal(root.get("id"), artist.getId())).build(currentSession).uniqueResultOptional();
return existingArtist.orElseGet(() -> {
Artist newArtist = new Artist(artist.getId(), artist.getName());
currentSession.persist(newArtist);
currentSession.flush();
return newArtist;
});
});
}
use of net.robinfriedli.exec.modes.MutexSyncMode in project aiode by robinfriedli.
the class ConnectCommand method awaitPrivateMessage.
private void awaitPrivateMessage(ClientSession clientSession, long userId, Guild guild, User user, int attemptNumber) {
CompletableFuture<PrivateMessageReceivedEvent> futurePrivateMessage = getManager().getEventWaiter().awaitEvent(PrivateMessageReceivedEvent.class, event -> event.getAuthor().getIdLong() == userId).orTimeout(1, TimeUnit.MINUTES);
CompletableFutures.handleWhenComplete(futurePrivateMessage, (event, error) -> {
try {
MessageService messageService = getMessageService();
if (event != null) {
String token = event.getMessage().getContentRaw();
UUID sessionId;
try {
sessionId = UUID.fromString(token);
} catch (IllegalArgumentException e) {
String message = String.format("'%s' is not a valid token. ", token);
if (attemptNumber >= RETRY_COUNT) {
messageService.sendError(message + "Maximum retry count reached.", user);
} else {
messageService.sendError(message + String.format("Attempt %d / %d", attemptNumber, RETRY_COUNT), user);
awaitPrivateMessage(clientSession, userId, guild, user, attemptNumber + 1);
}
return;
}
QueryBuilderFactory queryBuilderFactory = getQueryBuilderFactory();
MutexSyncMode<UUID> mutexSyncMode = new MutexSyncMode<>(sessionId, TOKEN_SYNC);
HibernateInvoker.create().invokeConsumer(Mode.create().with(mutexSyncMode), session -> {
Optional<GeneratedToken> foundGeneratedToken = queryBuilderFactory.find(GeneratedToken.class).where((cb, root) -> cb.equal(root.get("token"), sessionId)).build(session).uniqueResultOptional();
if (foundGeneratedToken.isEmpty()) {
String message = "Token is invalid. Make sure it matches the token generated by your web client. Tokens may not be shared or reused. ";
if (attemptNumber >= RETRY_COUNT) {
messageService.sendError(message + "Maximum retry count reached.", user);
} else {
messageService.sendError(message + String.format("Attempt %d / %d", attemptNumber, RETRY_COUNT), user);
awaitPrivateMessage(clientSession, userId, guild, user, attemptNumber + 1);
}
return;
}
GeneratedToken generatedToken = foundGeneratedToken.get();
Long existingSessionCount = queryBuilderFactory.select(ClientSession.class, (from, cb) -> cb.count(from.get("pk")), Long.class).where((cb, root) -> cb.equal(root.get("sessionId"), sessionId)).build(session).uniqueResult();
if (existingSessionCount > 0) {
messageService.sendError("A session with this ID already exists. You are probably already signed in, try reloading your web client. If your client still can't sign in try generating a new token using the reload button and then try the connect command again.", user);
return;
}
clientSession.setLastRefresh(LocalDateTime.now());
clientSession.setSessionId(sessionId);
clientSession.setIpAddress(generatedToken.getIp());
session.persist(clientSession);
session.delete(generatedToken);
messageService.sendSuccess(String.format("Okay, a session connected to guild '%s' " + "has been prepared. You may return to the web client to complete the setup. The client should " + "connect automatically, else you can continue manually.", guild.getName()), user);
});
} else if (error != null) {
if (error instanceof TimeoutException) {
messageService.sendError("Your connection attempt timed out", user);
} else {
EmbedBuilder exceptionEmbed = ExceptionUtils.buildErrorEmbed(error);
exceptionEmbed.setTitle("Exception");
exceptionEmbed.setDescription("There has been an error awaiting private message to establish connection");
LoggerFactory.getLogger(getClass()).error("unexpected exception while awaiting private message", error);
sendMessage(exceptionEmbed.build());
}
setFailed(true);
}
} finally {
USERS_WITH_PENDING_CONNECTION.remove(userId);
}
}, e -> {
LoggerFactory.getLogger(getClass()).error("Unexpected error in whenComplete of event handler", e);
EmbedBuilder embedBuilder = ExceptionUtils.buildErrorEmbed(e).setTitle("Exception").setDescription("There has been an unexpected exception while trying to establish the connection");
getMessageService().send(embedBuilder.build(), user);
});
}
use of net.robinfriedli.exec.modes.MutexSyncMode in project aiode by robinfriedli.
the class AbstractCommand method invoke.
protected <E> E invoke(Callable<E> callable) {
HibernateInvoker hibernateInvoker = HibernateInvoker.create(context.getSession());
Mode mode = Mode.create().with(new MutexSyncMode<>(context.getGuild().getIdLong(), GUILD_MUTEX_SYNC));
return hibernateInvoker.invoke(mode, callable);
}
use of net.robinfriedli.exec.modes.MutexSyncMode in project aiode by robinfriedli.
the class AbstractCommand method invokeWithSession.
protected <E> E invokeWithSession(Function<Session, E> function) {
HibernateInvoker hibernateInvoker = HibernateInvoker.create(context.getSession());
Mode mode = Mode.create().with(new MutexSyncMode<>(context.getGuild().getIdLong(), GUILD_MUTEX_SYNC));
return hibernateInvoker.invokeFunction(mode, function);
}
Aggregations