org.artifactory.storage.db.binstore.service.BlobBinaryProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.storage.db.binstore.service.BlobBinaryProviderImpl.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2012 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.storage.db.binstore.service;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.artifactory.api.context.ArtifactoryContext;
import org.artifactory.api.context.ContextHelper;
import org.artifactory.binstore.BinaryInfo;
import org.artifactory.io.checksum.Sha1Md5ChecksumInputStream;
import org.artifactory.storage.StorageException;
import org.artifactory.storage.binstore.service.BinaryInfoImpl;
import org.artifactory.storage.binstore.service.BinaryNotFoundException;
import org.artifactory.storage.binstore.service.base.BinaryProviderBase;
import org.artifactory.storage.binstore.service.annotation.BinaryProviderClassInfo;
import org.artifactory.storage.db.DbService;
import org.artifactory.storage.db.DbType;
import org.artifactory.storage.db.util.DbUtils;
import org.artifactory.storage.db.util.JdbcHelper;
import org.artifactory.storage.db.util.blob.BlobWrapperFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.ResultSet;
import java.sql.SQLException;

import static org.artifactory.storage.db.binstore.dao.BinariesDao.TEMP_SHA1_PREFIX;

/**
 * Date: 12/12/12
 * Time: 4:29 PM
 *
 * @author freds
 */
@BinaryProviderClassInfo(nativeName = "blob")
public class BlobBinaryProviderImpl extends BinaryProviderBase {
    private static final Logger log = LoggerFactory.getLogger(BlobBinaryProviderImpl.class);

    private JdbcHelper jdbcHelper;
    private DbType databaseType;
    private BlobWrapperFactory blobsFactory;

    @Override
    public void initialize() {
        ArtifactoryContext context = ContextHelper.get();
        this.databaseType = context.beanForType(DbService.class).getDatabaseType();
        this.blobsFactory = context.beanForType(BlobWrapperFactory.class);
        jdbcHelper = context.beanForType(JdbcHelper.class);
        if (jdbcHelper == null) {
            throw new IllegalArgumentException("Cannot create Blob binary provider without JDBC Helper!");
        }
    }

    @Override
    public boolean exists(String sha1) {
        try {
            long lengthFound;
            if (databaseType != DbType.MSSQL) {
                lengthFound = jdbcHelper.executeSelectLong("SELECT LENGTH(data) FROM binary_blobs where sha1 = ?",
                        sha1);
            } else { // mssql use len() function
                lengthFound = jdbcHelper
                        .executeSelectLong("SELECT DATALENGTH(data) FROM binary_blobs where sha1 = ?", sha1);
            }
            if (lengthFound != DbService.NO_DB_ID) {
                log.trace("Found sha1 {} with length {}", sha1);
                return true;
            }
        } catch (SQLException e) {
            throw new StorageException("Could not verify existence of " + sha1, e);
        }
        return next().exists(sha1);
    }

    @Nonnull
    @Override
    public InputStream getStream(String sha1) throws BinaryNotFoundException {
        try {
            /*if (TransactionSynchronizationManager.isSynchronizationActive()) {
            throw new StorageException("Cannot retrieve binary data of " +
                    sha1 + " since the datasource is in transaction!");
            }*/
            ResultSet rs = jdbcHelper.executeSelect("SELECT data FROM binary_blobs where sha1 = ?", sha1);
            if (rs.next()) {
                return new BlobStream(rs.getBinaryStream(1), rs);
            } else {
                DbUtils.close(rs);
            }
        } catch (SQLException e) {
            throw new StorageException("Could not select content for " + sha1, e);
        }
        return next().getStream(sha1);
    }

    @Nonnull
    @Override
    public BinaryInfo addStream(InputStream is) throws IOException {
        Sha1Md5ChecksumInputStream checksumStream = null;
        try {
            // first save to a temp file and calculate checksums while saving
            if (is instanceof Sha1Md5ChecksumInputStream) {
                checksumStream = (Sha1Md5ChecksumInputStream) is;
            } else {
                checksumStream = new Sha1Md5ChecksumInputStream(is);
            }
            // Create a dummy ID
            String randomId = TEMP_SHA1_PREFIX
                    + RandomStringUtils.randomAlphanumeric(40 - TEMP_SHA1_PREFIX.length());
            int inserted = jdbcHelper.executeUpdate("INSERT INTO binary_blobs VALUES (?,?)", randomId,
                    blobsFactory.create(checksumStream));
            if (inserted != 1) {
                throw new StorageException(
                        "Stream failed with unknown reason! Total line inserted was " + inserted);
            }
            checksumStream.close();
            BinaryInfo bd = new BinaryInfoImpl(checksumStream);
            log.trace("Inserting {} in blob binary provider", bd);

            String sha1 = bd.getSha1();
            if (!exists(sha1)) {
                int updated = 0;
                try {
                    updated = jdbcHelper.executeUpdate("UPDATE binary_blobs SET sha1 = ? WHERE sha1 = ?", sha1,
                            randomId);
                } catch (SQLException e) {
                    // May get duplicate key violated on concurrent insert
                    // TORE: [by fsi] with good SQL error handling should limit to duplicate key violation cases
                    log.debug("Got exception moving temp blob line " + randomId + " to " + sha1, e);
                    if (exists(sha1)) {
                        // All is OK someone else did the job already
                        deleteTempRow(randomId, sha1);
                        updated = 1;
                    }
                }
                if (updated != 1) {
                    deleteTempRow(randomId, sha1);
                    throw new StorageException("Could not update line " + randomId + " with " + sha1
                            + " ! Total line updated was " + updated);
                }
            } else {
                deleteTempRow(randomId, sha1);
            }

            return bd;
        } catch (SQLException e) {
            throw new StorageException("Could not insert Stream due to:" + e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(checksumStream);
        }
    }

    private void deleteTempRow(String randomId, String sha1) throws SQLException {
        int deleted = jdbcHelper.executeUpdate("DELETE FROM binary_blobs WHERE sha1 = ?", randomId);
        if (deleted != 1) {
            throw new StorageException("Deletion of temporary line " + randomId + " which match sha1='" + sha1
                    + "' did not return 1!\nTotal line deleted was " + deleted);
        }
    }

    @Override
    public boolean delete(String sha1) {
        try {
            int deleted = jdbcHelper.executeUpdate("DELETE FROM binary_blobs WHERE sha1 = ?", sha1);
            if (deleted != 1) {
                log.warn("Deletion of blob entry {} did not update any lines! Got {}", sha1, deleted);
            }
        } catch (SQLException e) {
            throw new StorageException("Could not insert Stream due to:" + e.getMessage(), e);
        }
        return next().delete(sha1);
    }

    static class BlobStream extends FilterInputStream {
        private final ResultSet resultSet;
        private boolean closed = false;

        BlobStream(InputStream in, ResultSet resultSet) {
            super(in);
            this.resultSet = resultSet;
        }

        @Override
        public void close() throws IOException {
            try {
                super.close();
            } finally {
                if (!closed) {
                    closed = true;
                    DbUtils.close(resultSet);
                }
            }
        }
    }
}