All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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