/** * Remove info related to the last command. Should be called when entering a stable state (UNKNOWN, CONNECTED, * DISCONNECTED). * * @return data without info related to the last command. */ public BaseClientData resetSession() { return new BaseClientData(connectionId, connection, connectionStatus, desiredConnectionStatus, connectionStatusDetails, inConnectionStatusSince, null, null); }
/** * @return the currently managed Connection */ protected final Connection connection() { return stateData().getConnection(); }
/** * @return the Connection Id */ protected final String connectionId() { return stateData().getConnectionId(); }
private BaseClientData setSession(final BaseClientData data, final ActorRef sender, final DittoHeaders headers) { if (!Objects.equals(sender, getSelf()) && !Objects.equals(sender, getContext().system().deadLetters())) { return data.setSessionSender(sender) .setSessionHeaders(headers); } else { return data.resetSession(); } }
private State<BaseClientState, BaseClientData> clientConnected(final ClientConnected clientConnected, final BaseClientData data) { return ifEventUpToDate(clientConnected, () -> { LogUtil.enhanceLogWithCustomField(log, BaseClientData.MDC_CONNECTION_ID, connectionId()); startMessageMappingProcessor(data.getConnection().getMappingContext().orElse(null)); allocateResourcesOnConnection(clientConnected); data.getSessionSender().ifPresent(origin -> origin.tell(new Status.Success(CONNECTED), getSelf())); return goTo(CONNECTED).using(data.resetSession() .setConnectionStatus(ConnectionStatus.OPEN) .setConnectionStatusDetails("Connected at " + Instant.now())); }); }
private FSM.State<BaseClientState, BaseClientData> closeConnection(final CloseConnection closeConnection, final BaseClientData data) { final ActorRef sender = getSender(); doDisconnectClient(data.getConnection(), sender); return goTo(DISCONNECTING).using(setSession(data, sender, closeConnection.getDittoHeaders()) .setDesiredConnectionStatus(ConnectionStatus.CLOSED) .setConnectionStatusDetails("closing or deleting connection at " + Instant.now())); }
private State<BaseClientState, BaseClientData> onUnknownEvent(final Object event, final BaseClientData state) { log.warning("received unknown/unsupported message {} in state {} - status: {} - sender: {}", event, stateName(), state.getConnectionStatus() + ": " + state.getConnectionStatusDetails().orElse(""), getSender()); final ActorRef sender = getSender(); if (!Objects.equals(sender, getSelf()) && !Objects.equals(sender, getContext().system().deadLetters())) { sender.tell(unhandledExceptionForSignalInState(event, stateName()), getSelf()); } return stay(); }
private FSM.State<BaseClientState, BaseClientData> openConnection(final OpenConnection openConnection, final BaseClientData data) { final ActorRef sender = getSender(); final Connection connection = data.getConnection(); final DittoHeaders dittoHeaders = openConnection.getDittoHeaders(); if (canConnectViaSocket(connection)) { doConnectClient(connection, sender); return goTo(CONNECTING).using(setSession(data, sender, dittoHeaders)); } else { cleanupResourcesForConnection(); final DittoRuntimeException error = newConnectionFailedException(data.getConnection(), dittoHeaders); sender.tell(new Status.Failure(error), getSelf()); return goTo(UNKNOWN).using(data.resetSession()); } }
/** * Creates the handler for messages in testing state. * Overwrite and extend by additional matchers. * * @return an FSM function builder */ protected FSMStateFunctionBuilder<BaseClientState, BaseClientData> inTestingState() { return matchEvent(Status.Status.class, (e, d) -> Objects.equals(getSender(), getSelf()), (status, data) -> { final Status.Status answerToPublish = getStatusToReport(status); data.getSessionSender().ifPresent(sender -> sender.tell(answerToPublish, getSelf())); return stop(); }) .eventEquals(StateTimeout(), BaseClientData.class, (stats, data) -> { log.info("test timed out."); data.getSessionSender().ifPresent(sender -> { final DittoRuntimeException error = ConnectionFailedException.newBuilder(connectionId()) .description(String.format("Failed to open requested connection within <%d> seconds!", TEST_CONNECTION_TIMEOUT)) .dittoHeaders(data.getSessionHeaders()) .build(); sender.tell(new Status.Failure(error), getSelf()); }); return stop(); }); }
/** * Start MQTT publisher and subscribers, expect "Status.Success" from each of them, then send "ClientConnected" to * self. * * @param connection connection of the publisher and subscribers. * @param dryRun if set to true, exchange no message between the broker and the Ditto cluster. */ private void connectClient(final Connection connection, final boolean dryRun) { final MqttConnectionFactory factory = connectionFactoryCreator.apply(connection, stateData().getSessionHeaders()); // start publisher startMqttPublisher(factory, dryRun); // start consumers if (isConsuming()) { // start message mapping processor actor early so that consumer streams can be run final Either<DittoRuntimeException, ActorRef> messageMappingProcessor = startMessageMappingProcessor(); if (messageMappingProcessor.isLeft()) { final DittoRuntimeException e = messageMappingProcessor.left().get(); log.warning("failed to start mapping processor due to {}", e); } else { final ActorRef mappingActor = messageMappingProcessor.right().get(); // start new KillSwitch for the next batch of consumers refreshConsumerKillSwitch(KillSwitches.shared("consumerKillSwitch")); connection().getSources().forEach(source -> startMqttConsumers(factory, mappingActor, source, dryRun)); } } else { log.info("Not starting consumption because there is no source."); } }
private Instant getInConnectionStatusSince() { return stateData().getInConnectionStatusSince(); }
private ConnectionStatus getCurrentConnectionStatus() { return stateData().getConnectionStatus(); }
private Optional<String> getCurrentConnectionStatusDetails() { return stateData().getConnectionStatusDetails(); }
private FSMStateFunctionBuilder<BaseClientState, BaseClientData> inUnknownState() { return matchEvent(OpenConnection.class, BaseClientData.class, this::openConnection) .event(CloseConnection.class, BaseClientData.class, this::closeConnection) .event(TestConnection.class, BaseClientData.class, this::testConnection) .eventEquals(StateTimeout(), BaseClientData.class, (state, data) -> { if (ConnectionStatus.OPEN == data.getDesiredConnectionStatus()) { log.info("Did not receive connect command within init-timeout, connecting"); final OpenConnection openConnection = OpenConnection.of(connectionId(), DittoHeaders.empty()); getSelf().tell(openConnection, getSelf()); } else if (ConnectionStatus.CLOSED == data.getDesiredConnectionStatus()) { log.info( "Did not receive connect command within init-timeout, desired state is closed, going to disconnected state."); return goTo(DISCONNECTED); } else { log.info( "Did not receive connect command within init-timeout, desired state is {}, do nothing.", data.getDesiredConnectionStatus()); } return stay(); // handle self-told commands later }); }
private State<BaseClientState, BaseClientData> clientConnected(final ClientConnected clientConnected, final BaseClientData data) { return ifEventUpToDate(clientConnected, () -> { LogUtil.enhanceLogWithCustomField(log, BaseClientData.MDC_CONNECTION_ID, connectionId()); startMessageMappingProcessor(data.getConnection().getMappingContext().orElse(null)); allocateResourcesOnConnection(clientConnected); data.getSessionSender().ifPresent(origin -> origin.tell(new Status.Success(CONNECTED), getSelf())); return goTo(CONNECTED).using(data.resetSession() .setConnectionStatus(ConnectionStatus.OPEN) .setConnectionStatusDetails("Connected at " + Instant.now())); }); }
private BaseClientData setSession(final BaseClientData data, final ActorRef sender, final DittoHeaders headers) { if (!Objects.equals(sender, getSelf()) && !Objects.equals(sender, getContext().system().deadLetters())) { return data.setSessionSender(sender) .setSessionHeaders(headers); } else { return data.resetSession(); } }
private FSM.State<BaseClientState, BaseClientData> closeConnection(final CloseConnection closeConnection, final BaseClientData data) { final ActorRef sender = getSender(); doDisconnectClient(data.getConnection(), sender); return goTo(DISCONNECTING).using(setSession(data, sender, closeConnection.getDittoHeaders()) .setDesiredConnectionStatus(ConnectionStatus.CLOSED) .setConnectionStatusDetails("closing or deleting connection at " + Instant.now())); }
private State<BaseClientState, BaseClientData> onUnknownEvent(final Object event, final BaseClientData state) { Object message = event; if (event instanceof Failure) { message = ((Failure) event).cause(); } if (event instanceof Status.Failure) { message = ((Status.Failure) event).cause(); } if (message instanceof Throwable) { log.error((Throwable) message, "received Exception {} in state {} - status: {} - sender: {}", message, stateName(), state.getConnectionStatus() + ": " + state.getConnectionStatusDetails().orElse(""), getSender()); } else { log.warning("received unknown/unsupported message {} in state {} - status: {} - sender: {}", message, stateName(), state.getConnectionStatus() + ": " + state.getConnectionStatusDetails().orElse(""), getSender()); } final ActorRef sender = getSender(); if (!Objects.equals(sender, getSelf()) && !Objects.equals(sender, getContext().system().deadLetters())) { sender.tell(unhandledExceptionForSignalInState(event, stateName()), getSelf()); } return stay(); }
private FSM.State<BaseClientState, BaseClientData> openConnection(final OpenConnection openConnection, final BaseClientData data) { final ActorRef sender = getSender(); final Connection connection = data.getConnection(); final DittoHeaders dittoHeaders = openConnection.getDittoHeaders(); if (canConnectViaSocket(connection)) { doConnectClient(connection, sender); return goTo(CONNECTING).using(setSession(data, sender, dittoHeaders)); } else { cleanupResourcesForConnection(); final DittoRuntimeException error = newConnectionFailedException(data.getConnection(), dittoHeaders); sender.tell(new Status.Failure(error), getSelf()); return goTo(UNKNOWN).using(data.resetSession()); } }
/** * Creates the handler for messages in testing state. * Overwrite and extend by additional matchers. * * @return an FSM function builder */ protected FSMStateFunctionBuilder<BaseClientState, BaseClientData> inTestingState() { return matchEvent(Status.Status.class, (e, d) -> Objects.equals(getSender(), getSelf()), (status, data) -> { final Status.Status answerToPublish = getStatusToReport(status); data.getSessionSender().ifPresent(sender -> sender.tell(answerToPublish, getSelf())); return stop(); }) .eventEquals(StateTimeout(), BaseClientData.class, (stats, data) -> { log.info("test timed out."); data.getSessionSender().ifPresent(sender -> { final DittoRuntimeException error = ConnectionFailedException.newBuilder(connectionId()) .description(String.format("Failed to open requested connection within <%d> seconds!", TEST_CONNECTION_TIMEOUT)) .dittoHeaders(data.getSessionHeaders()) .build(); sender.tell(new Status.Failure(error), getSelf()); }); return stop(); }); }