Untitled
package ru.crystals.sco3.business.process.dialogs; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import ru.crystals.sco3.business.entity.menu.RequestInformationFromUser; import ru.crystals.sco3.business.entity.menu.UserInformationInputCompleted; import ru.crystals.sco3.business.entity.menu.UserInformationType; import ru.crystals.sco3.interfaces.process.api.ProcessAPI; import ru.crystals.sco3.utilities.interfaces.service_executor.ServiceExecutor; import javax.annotation.PostConstruct; import java.util.Deque; import java.util.LinkedList; import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; @Slf4j @Service @RequiredArgsConstructor public class DialogManager { // нужен чтобы не допустить параллельного выполнения операций processAPI.runProcess(dialogProcess) и processAPI.endProcess(dialogProcess) private final Object lock = new Object(); private final ServiceExecutor executor; private final ProcessAPI processAPI; private final Deque<DialogDescription> dialogs = new LinkedList<>(); private final AtomicLong dialogId = new AtomicLong(); private DialogProcess dialogProcess; private DialogDescription currentDialog; @PostConstruct private void postConstruct() { executor.execute(this::showDialogLoop, log, "ShowDialogLoop_Thread"); } private void showDialogLoop() { try { while (!Thread.currentThread().isInterrupted()) { synchronized (lock) { DialogDescription dd = null; boolean empty = false; while ((dd = currentDialog) != null || (empty = dialogs.isEmpty())) { log.info("wait, currentDialog {}, dialogs.isEmpty() {}", dd, empty); lock.wait(); } DialogDescription dialog = dialogs.pollFirst(); log.info("Show dialog with id {}", dialog.request().id()); currentDialog = dialog; processAPI.runProcess(null, dialogProcess, dialog); } } } catch (Exception e) { log.error(e.getMessage(), e); Thread.currentThread().interrupt(); } } public void closeDialog(String id) { synchronized (lock) { DialogDescription current = currentDialog; if (current == null) { return; } if (current.request().id().equals(id)) { log.info("Close current dialog with id {}", id); currentDialog = null; processAPI.endProcess(dialogProcess, null); log.info("notify close current"); lock.notify(); return; } if (dialogs.removeIf(d -> d.request().id().equals(id))) { log.info("Close waiting dialog with id {}, removed from dialogs.", id); return; } log.warn("Trying to close not existing dialog, {}", id); } } public void showDialog(RequestInformationFromUser r, Consumer<UserInformationInputCompleted> answerCallback) { synchronized (lock) { boolean callAttendant = callAttendant(r); log.info("Dialog adding to queue, dialogId={}", r.id()); dialogs.add(new DialogDescription(r, answerCallback, callAttendant)); log.info("notify show"); lock.notify(); } } private boolean callAttendant(RequestInformationFromUser request) { return UserInformationType.REQUEST_AGE_CONFIRMATION == request.type() && Objects.isNull(request.requestAttendant()); } public String getNextDialogId() { return String.valueOf(dialogId.getAndIncrement()); } public void setDialogProcess(DialogProcess dialogProcess) { this.dialogProcess = dialogProcess; } public void reset() { synchronized (lock) { log.info("Reset dialog manager state"); dialogs.clear(); currentDialog = null; processAPI.endProcess(dialogProcess, null); lock.notify(); } } public boolean isDialogExist(String dialogId) { return dialogs.stream().anyMatch(dd -> dd.request().id().equals(dialogId)) || currentDialog.request().id().equals(dialogId); } }
Leave a Comment