io.vlingo.xoom.symbio.store.journal.jdbc.JDBCDispatcherControlDelegate Maven / Gradle / Ivy
// Copyright © 2012-2021 VLINGO LABS. All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL
// was not distributed with this file, You can obtain
// one at https://mozilla.org/MPL/2.0/.
package io.vlingo.xoom.symbio.store.journal.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import io.vlingo.xoom.actors.Logger;
import io.vlingo.xoom.common.serialization.JsonSerialization;
import io.vlingo.xoom.symbio.BaseEntry;
import io.vlingo.xoom.symbio.Entry;
import io.vlingo.xoom.symbio.Metadata;
import io.vlingo.xoom.symbio.State;
import io.vlingo.xoom.symbio.store.StoredTypes;
import io.vlingo.xoom.symbio.store.common.jdbc.Configuration;
import io.vlingo.xoom.symbio.store.common.jdbc.DatabaseType;
import io.vlingo.xoom.symbio.store.dispatch.Dispatchable;
import io.vlingo.xoom.symbio.store.dispatch.DispatcherControl;
public class JDBCDispatcherControlDelegate implements DispatcherControl.DispatcherControlDelegate, State.TextState> {
static final String DISPATCHEABLE_ENTRIES_DELIMITER = "|";
private final Connection connection;
private final DatabaseType databaseType;
private final Logger logger;
private final PreparedStatement selectDispatchables;
private final JDBCQueries queries;
public JDBCDispatcherControlDelegate(final Configuration configuration, final Logger logger) throws SQLException {
this.connection = configuration.connection;
this.databaseType = configuration.databaseType;
this.logger = logger;
this.queries = JDBCQueries.queriesFor(configuration.connection);
queries.createTables();
this.selectDispatchables = queries.prepareSelectDispatchablesQuery(configuration.originatorId);
}
@Override
public Collection, State.TextState>> allUnconfirmedDispatchableStates() throws Exception {
final List, State.TextState>> dispatchables = new ArrayList<>();
try (final ResultSet result = selectDispatchables.executeQuery()) {
while (result.next()) {
dispatchables.add(dispatchableFrom(result));
}
doCommit();
} catch (Exception e) {
logger.error("xoom-symbio-jdbc-" + databaseType + ": Failed to query all unconfirmed dispatchables because: " + e.getMessage(), e);
fail();
}
return dispatchables;
}
@Override
public void confirmDispatched(final String dispatchId) {
try {
queries.prepareDeleteDispatchableQuery(dispatchId).executeUpdate();
doCommit();
} catch (final Exception e) {
logger.error("xoom-symbio-jdbc-" + databaseType + ": Failed to confirm dispatch with id " + dispatchId + " because: " + e.getMessage(), e);
fail();
}
}
@Override
public void stop() {
try {
queries.close();
} catch (final SQLException e) {
//ignore
}
}
private void doCommit() {
try {
connection.commit();
} catch (final SQLException e) {
logger.error("xoom-symbio-jdbc-" + databaseType + ": Could not complete transaction", e);
fail();
throw new IllegalStateException(e);
}
}
private void fail() {
try {
connection.rollback();
} catch (final Exception e) {
logger.error(getClass().getSimpleName() + ": Rollback failed because: " + e.getMessage(), e);
}
}
private Dispatchable, State.TextState> dispatchableFrom(final ResultSet resultSet) throws SQLException, ClassNotFoundException {
final String dispatchId = resultSet.getString(1);
LocalDateTime createdOn =
LocalDateTime.ofInstant(
Instant.ofEpochMilli(resultSet.getLong(2)),
ZoneId.systemDefault());
final State.TextState state;
final String stateId = resultSet.getString(3);
if (stateId != null && !stateId.isEmpty()) {
final String data = resultSet.getString(4);
final int dataVersion = resultSet.getInt(5);
final Class> type = StoredTypes.forName(resultSet.getString(6));
final int typeVersion = resultSet.getInt(7);
final String metadataValue = resultSet.getString(8);
final Metadata metadata = JsonSerialization.deserialized(metadataValue, Metadata.class);
state = new State.TextState(stateId, type, typeVersion, data, dataVersion, metadata);
} else {
state = null;
}
final String entriesIds = resultSet.getString(9);
final List> entries = new ArrayList<>();
if (entriesIds != null && !entriesIds.isEmpty()) {
final String[] ids = entriesIds.split("\\" + DISPATCHEABLE_ENTRIES_DELIMITER);
for (final String entryId : ids) {
try (final ResultSet result = queries.prepareSelectEntryQuery(Long.parseLong(entryId)).executeQuery()) {
if (result.next()) {
entries.add(entryFrom(result));
}
}
}
}
return new Dispatchable<>(dispatchId, createdOn, state, entries);
}
private Entry entryFrom(final ResultSet resultSet) throws SQLException, ClassNotFoundException {
final String id = resultSet.getString(1);
final String entryData = resultSet.getString(2);
final String entryType = resultSet.getString(3);
final int eventTypeVersion = resultSet.getInt(4);
final String entryMetadata = resultSet.getString(5);
final int entryVersion = resultSet.getInt(6); // from E_STREAM_VERSION
final Class> classOfEvent = StoredTypes.forName(entryType);
final Metadata metadata = JsonSerialization.deserialized(entryMetadata, Metadata.class);
return new BaseEntry.TextEntry(id, classOfEvent, eventTypeVersion, entryData, entryVersion, metadata);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy