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

org.nuiton.topia.service.sql.internal.SqlRequestSetConsumerContext Maven / Gradle / Ivy

The newest version!
package org.nuiton.topia.service.sql.internal;

/*-
 * #%L
 * ToPIA Extension :: API
 * %%
 * Copyright (C) 2018 - 2022 Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.collect.Multimap;
import com.zaxxer.hikari.pool.HikariProxyConnection;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.nuiton.topia.persistence.HibernateAvailableSettings;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaException;
import org.nuiton.topia.persistence.TopiaPersistenceContext;
import org.nuiton.topia.persistence.internal.AbstractTopiaApplicationContext;
import org.nuiton.topia.persistence.internal.AbstractTopiaPersistenceContext;
import io.ultreia.java4all.util.sql.SqlScriptWriter;
import io.ultreia.java4all.util.sql.BlobsContainer;
import org.nuiton.topia.service.sql.blob.TopiaEntitySqlBlob;
import org.nuiton.topia.service.sql.blob.TopiaEntitySqlBlobModel;
import org.nuiton.topia.service.sql.model.TopiaEntitySqlSelectArgument;
import org.postgresql.PGConnection;
import org.postgresql.jdbc.PgConnection;
import org.postgresql.largeobject.LargeObject;
import org.postgresql.largeobject.LargeObjectManager;

import javax.sql.rowset.serial.SerialBlob;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

/**
 * Created on 25/09/2020.
 *
 * @author Tony Chemit - [email protected]
 * @since 1.28
 */
public class SqlRequestSetConsumerContext implements Closeable {

    private static final Logger log = LogManager.getLogger(SqlRequestSetConsumerContext.class);
    private final SqlRequestSet request;
    private final AbstractTopiaApplicationContext sourceTopiaApplicationContext;
    private final Set blobsContainerBuilders;
    private final TopiaEntitySqlBlobModel blobModel;
    private TopiaPersistenceContext sourcePersistenceContext;
    private SqlScriptWriter writer;
    private Date date;

    public static String ids(TopiaEntitySqlSelectArgument selectArgument) {
        if (selectArgument == null) {
            return null;
        }
        String ids;
        Set filterIds = selectArgument.getIds();
        if (filterIds.size() == 1) {
            ids = " = '" + filterIds.iterator().next() + "'";
        } else {
            StringBuilder in = new StringBuilder();
            filterIds.stream().map(id -> String.format(", '%s'", id)).forEach(in::append);
            ids = String.format(" IN ( %s )", in.substring(2));
        }
        return ids;
    }

    public SqlRequestSetConsumerContext(AbstractTopiaApplicationContext sourceTopiaApplicationContext, SqlRequestSet request) {
        this.request = request;
        this.sourceTopiaApplicationContext = sourceTopiaApplicationContext;
        this.blobModel = request.getBlobModel();
        this.blobsContainerBuilders = new LinkedHashSet<>();
    }

    public String getNewParentId() {
        return request.getNewParentId();
    }

    public SqlRequestSet getRequest() {
        return request;
    }

    public AbstractTopiaApplicationContext getSourceTopiaApplicationContext() {
        return sourceTopiaApplicationContext;
    }

    public String ids(TopiaEntitySqlSelectArgument selectArgument, boolean optimize) {
        if (optimize) {
            selectArgument = checkArgument(selectArgument);
        }
        return ids(selectArgument);
    }

    public TopiaEntitySqlSelectArgument checkArgument(TopiaEntitySqlSelectArgument selectArgument) {
        if (selectArgument != null) {
            // find out if we are deleting all data
            Set ids = selectArgument.getIds();
            String firstId = ids.iterator().next();
            Class entityClass = getSourceTopiaApplicationContext().getConfiguration().getTopiaIdFactory().getClassName(firstId);
            long count = getSourcePersistenceContext().getDao(entityClass).count();
            if (count == ids.size()) {
                // for all data : no select argument required
                selectArgument = null;
            }
        }
        return selectArgument;
    }

    public void addSchemaExportScript(Class dialectType, SchemaExport.Action action) throws IOException {

        Path sqlScriptFile = Files.createTempFile(request.getPath().getParent(), "schemaExport_" + action.name() + "_", ".sql");
        try {

            Metadata hibernateMetadata = getSourcePersistenceContext().getHibernateSupport().getHibernateMetadata();

            StandardServiceRegistry serviceRegistry = ((MetadataImplementor) hibernateMetadata).getMetadataBuildingOptions().getServiceRegistry();

            Properties properties = new Properties();
            properties.putAll(serviceRegistry.getService(ConfigurationService.class).getSettings());
            properties.put(HibernateAvailableSettings.DIALECT, dialectType.getName());

            BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();
            StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder(bsr);
            ssrBuilder.applySettings(properties);
            StandardServiceRegistry registry = ssrBuilder.build();

            try {
                new SchemaExport()
                        .setOutputFile(sqlScriptFile.toFile().getAbsolutePath())
                        .setDelimiter(";")
                        .execute(EnumSet.of(TargetType.SCRIPT), action, hibernateMetadata, registry);
            } finally {
                StandardServiceRegistryBuilder.destroy(registry);
            }

            getWriter().writeScript(sqlScriptFile);
        } finally {
            if (Files.exists(sqlScriptFile)) {
                Files.delete(sqlScriptFile);
            }
        }

    }

    public AbstractTopiaPersistenceContext getSourcePersistenceContext() {
        if (sourcePersistenceContext == null) {
            sourcePersistenceContext = sourceTopiaApplicationContext.newPersistenceContext();
        }
        return (AbstractTopiaPersistenceContext) sourcePersistenceContext;
    }

    @Override
    public void close() throws IOException {
        try {
            if (sourcePersistenceContext != null) {
                sourcePersistenceContext.close();
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    public SqlScriptWriter getWriter() {
        if (writer == null) {
            SqlScriptWriter.Builder writerBuilder = SqlScriptWriter.builder(request.getPath());
            if (request.isGzip()) {
                writerBuilder.gzip();
            }
            writer = writerBuilder.build();
        }

        return writer;
    }

    public Optional> getBlobsContainersBuilder() {
        if (blobsContainerBuilders.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(blobsContainerBuilders);
    }

    public void registerBlobsContainer(BlobsContainer.Builder builder) {
        blobsContainerBuilders.add(builder);
    }

    public int getReadFetchSize() {
        return request.getReadFetchSize();
    }

    public void flushBlobs(Map blobsContainersBuilder) {
        Optional> optionalBlobsContainers = getBlobsContainersBuilder();
        if (optionalBlobsContainers.isPresent()) {
            Set blobsContainers = optionalBlobsContainers.get();
            for (BlobsContainer.Builder blobsContainerBuilder : blobsContainers) {
                String tableName = blobsContainerBuilder.getTableName();
                BlobsContainer blobsContainer = blobsContainerBuilder.build();
                BlobsContainer existingBlobsContainer = blobsContainersBuilder.get(tableName);
                if (existingBlobsContainer != null) {
                    existingBlobsContainer.add(blobsContainer.getBlobsById());
                } else {
                    blobsContainersBuilder.put(tableName, blobsContainer);
                }
            }
        }
    }

    public Map initBlobsBuilder(boolean useBlob, String schemaAndTableName) {
        Map result = new TreeMap<>();
        if (useBlob) {
            TopiaEntitySqlBlob blobModel = this.blobModel.getEntries().values().stream().filter(m -> schemaAndTableName.equals(m.schemaAndTableName())).findFirst().orElseThrow();
            List blobColumns = blobModel.getColumnNames();
            for (String blobColumn : blobColumns) {
                BlobsContainer.Builder blobBuilder = BlobsContainer.builder(schemaAndTableName, blobColumn);
                registerBlobsContainer(blobBuilder);
                result.put(blobColumn, blobBuilder);
            }
        }
        return result;
    }

    public void copyBlob(PreparedStatement preparedStatement, String topiaId, Object columnValue,
                         BlobsContainer.Builder blobsBuilder) throws SQLException {
        if (columnValue == null) {
            return;
        }
        Connection connection = preparedStatement.getConnection();
        InputStream stream;
        int size;
        if (columnValue instanceof Long) {
            PGConnection pgConnection;
            if (connection instanceof PgConnection) {
                pgConnection = (PGConnection) connection;
            } else if (connection instanceof HikariProxyConnection) {
                HikariProxyConnection connection1 = (HikariProxyConnection) connection;
                try {
                    Field field = connection1.getClass().getSuperclass().getDeclaredField("delegate");
                    field.setAccessible(true);
                    pgConnection = (PGConnection) field.get(connection1);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                throw new IllegalStateException("Can't know how to manage connection: " + connection);
            }
            LargeObjectManager largeObjectAPI = pgConnection.getLargeObjectAPI();
            LargeObject obj = largeObjectAPI.open((long) columnValue, LargeObjectManager.READ);
            size = obj.size();
            stream = obj.getInputStream();
        } else {
            Blob blob = (Blob) columnValue;
            stream = blob.getBinaryStream();
            SerialBlob serialBlob = new SerialBlob(blob);
            size = (int) serialBlob.length();
        }
        try (ByteArrayOutputStream stringWriter = new ByteArrayOutputStream(size)) {
            stringWriter.write(stream);
            blobsBuilder.addBlob(topiaId, stringWriter.toByteArray());
            log.debug("Add blob: " + topiaId);
        } catch (IOException e) {
            throw new TopiaException("Can't add blob", e);
        }
    }

    public List generateOrder(Multimap parents, String sentinel) {
        List result = new LinkedList<>();
        generateOrder(parents, parents.get(sentinel), result);
        return result;
    }

    public Date now() {
        if (date == null) {
            date = new Date();
        }
        return date;
    }

    private void generateOrder(Multimap parents, Collection toDo, List result) {
        for (String id : toDo) {
            if (!result.contains(id)) {
                log.debug(String.format("Add id: %s (pos %d)", id, result.size()));
                result.add(id);
            }
            Collection topiaIds = parents.get(id);
            generateOrder(parents, topiaIds, result);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy