gov.nih.nci.caarray.upgrade.FixHybridizationsWithMissingArraysMigrator.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.caarray.upgrade.FixHybridizationsWithMissingArraysMigrator.java

Source

//======================================================================================
// Copyright 5AM Solutions Inc, Yale University
//
// Distributed under the OSI-approved BSD 3-Clause License.
// See http://ncip.github.com/caarray/LICENSE.txt for details.
//======================================================================================
package gov.nih.nci.caarray.upgrade;

import gov.nih.nci.caarray.dao.DaoModule;
import gov.nih.nci.caarray.domain.LSID;
import gov.nih.nci.caarray.domain.MultiPartBlob;
import gov.nih.nci.caarray.domain.array.ArrayDesign;
import gov.nih.nci.caarray.domain.file.CaArrayFile;
import gov.nih.nci.caarray.domain.file.FileType;
import gov.nih.nci.caarray.platforms.PlatformModule;
import gov.nih.nci.caarray.platforms.SessionTransactionManager;
import gov.nih.nci.caarray.platforms.SessionTransactionManagerNoOpImpl;
import gov.nih.nci.caarray.platforms.spi.DataFileHandler;
import gov.nih.nci.caarray.platforms.spi.PlatformFileReadException;
import gov.nih.nci.caarray.util.CaArrayHibernateHelper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import liquibase.database.Database;
import liquibase.exception.CustomChangeException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.UnhandledException;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Types;

/**
 * Migrator to find hybridizations with missing arrays, and where possible, create them based on array designs specified
 * in data files or the experiment.
 * 
 * The migrator will use the array design for the experiment, if there is only one. Otherwise, it will extract the array
 * design from one of the data files associated with the hybridization, if present. If none of the data files specify
 * the array design, and the experiment has more than one array design specified, then no action will be taken.
 * 
 * @author Dan Kokotov
 */
@SuppressWarnings({ "PMD.TooManyMethods", "PMD.CyclomaticComplexity" })
public class FixHybridizationsWithMissingArraysMigrator extends AbstractCustomChange {
    private Database database;
    private Set<DataFileHandler> handlers;
    private final Map<Long, File> openFileMap = new HashMap<Long, File>();
    private final FixHybridizationsWithMissingArraysDao dao;
    private Injector injector;

    /**
     * Creates a FixHybridizationsWithMissingArraysMigrator that will make updates to the live database.
     */
    public FixHybridizationsWithMissingArraysMigrator() {
        this(new FixHybridizationsWithMissingArraysDao());
    }

    /**
     * Creates a FixHybridizationsWithMissingArraysMigrator that uses a custom data access object for
     * database operations.  Intended for use in testing.
     * 
     * @param dao handles the database update operations
     */
    public FixHybridizationsWithMissingArraysMigrator(FixHybridizationsWithMissingArraysDao dao) {
        this.dao = dao;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doExecute(Database db) throws CustomChangeException {
        try {
            final Connection underlyingConnection = db.getConnection().getUnderlyingConnection();
            execute(underlyingConnection);
        } catch (Exception e) {
            throw new CustomChangeException(e);
        }
    }

    /**
     * Execute the change given a connection.
     * @param connection the connection
     */
    public void execute(Connection connection) {
        setup(connection);
        try {
            List<Long> hybIdsWithoutArray = dao.getHybIdsWithNoArrayOrNoArrayDesign();
            for (Long hid : hybIdsWithoutArray) {
                ensureArrayDesignSetForHyb(hid);
            }
        } catch (Exception e) {
            throw new UnhandledException("Could not fix hybridizations", e);
        }
    }

    private void ensureArrayDesignSetForHyb(Long hid) throws SQLException, IOException, PlatformFileReadException {
        Long adid = getArrayDesignId(hid);
        if (adid != null) {
            setArrayDesignForHyb(hid, adid);
        }
    }

    private Long getArrayDesignId(Long hid) throws SQLException, IOException, PlatformFileReadException {
        Long adid = dao.getUniqueArrayDesignIdFromExperiment(hid);
        if (adid == null) {
            adid = getArrayDesignIdFromFiles(hid);
        }
        return adid;
    }

    private Long getArrayDesignIdFromFiles(Long hid) throws SQLException, IOException, PlatformFileReadException {
        List<Long> dataFileIds = dao.getImportedDataFileIdsFromHybId(hid);
        for (Long fileId : dataFileIds) {
            Long adid = getArrayDesignFromFile(fileId);
            if (adid != null) {
                return adid;
            }
        }
        return null;
    }

    @SuppressWarnings("deprecation")
    private Long getArrayDesignFromFile(Long fileId) throws SQLException, IOException {
        FileType ft = dao.getFileType(fileId);

        if (!ft.isArrayData()) {
            return null;
        }

        CaArrayFile caArrayFile = new CaArrayFile();
        caArrayFile.setId(fileId);
        DataFileHandler handler = null;
        try {
            handler = getHandler(caArrayFile);
            return findArrayDesignFromFile(handler).getId();
        } catch (PlatformFileReadException e) {
            throw new IllegalArgumentException("Error reading file " + caArrayFile.getName(), e);
        } finally {
            if (handler != null) {
                handler.closeFiles();
            }
        }
    }

    private ArrayDesign findArrayDesignFromFile(DataFileHandler handler) throws PlatformFileReadException {
        List<LSID> designLsids = handler.getReferencedArrayDesignCandidateIds();
        return dao.getFirstArrayDesignFromLsidList(designLsids);
    }

    private void setArrayDesignForHyb(Long hid, Long adid) throws SQLException {
        Long aid = dao.getArrayFromHybridizationId(hid);
        if (aid != null) {
            dao.setArrayDesignForArray(aid, adid);
        } else {
            dao.setArrayAndDesign(hid, adid);
        }
    }

    /**
     * Find the appropriate data handler for the given data file, and initialize it.
     * 
     * @param caArrayFile the data file to be processed
     * @return the DataFileHandler instance capable of processing that file. That handler will have been initialized
     *         with this file.
     */
    private DataFileHandler getHandler(CaArrayFile caArrayFile) {
        for (DataFileHandler handler : this.handlers) {
            try {
                if (handler.openFile(caArrayFile)) {
                    return handler;
                }
            } catch (PlatformFileReadException e) {
                handler.closeFiles();
                throw new IllegalArgumentException("Error reading file " + caArrayFile.getName(), e);
            }
        }
        throw new IllegalArgumentException("Unsupported type " + caArrayFile.getFileType());
    }

    private void setup(Connection connection) {
        this.injector = createInjector();
        dao.setConnection(connection);
        createHibernateHelper(connection);
        this.openFileMap.clear();
        this.handlers = getHandlers();
    }

    private Injector createInjector() {
        return Guice.createInjector(new DaoModule(), new PlatformModule(), getLocalModule());
    }

    private AbstractModule getLocalModule() {
        final AbstractModule localModule = new AbstractModule() {
            @Override
            protected void configure() {
                // TODO: may need to bind a local implementation of storage in place of this: bind(FileManager.class).toInstance(createFileManager());               
                bind(SessionTransactionManager.class).toInstance(new SessionTransactionManagerNoOpImpl());
                bind(CaArrayHibernateHelper.class).toInstance(new SingleConnectionHibernateHelper());
            }
        };
        return localModule;
    }

    // TODO: remove this when appropriate local implementation of storage is created to replace this.  
    // Until then, keeping it here temporarily as a reference. - A Sy 2011-03-30    
    //    private FileManager createFileManager() {
    //        return new FileManager() {
    //            public File openFile(CaArrayFile caArrayFile) {
    //                try {
    //                    File file = getFile(caArrayFile.getId());
    //                    openFileMap.put(caArrayFile.getId(), file);
    //                    return file;
    //                } catch (SQLException e) {
    //                    throw new IllegalStateException("Could not open the file " + caArrayFile);
    //                } catch (IOException e) {
    //                    throw new IllegalStateException("Could not open the file " + caArrayFile);
    //                }
    //            }
    //
    //            public void closeFile(CaArrayFile caArrayFile) {
    //                File file = openFileMap.get(caArrayFile.getId());
    //                FileUtils.deleteQuietly(file);                            
    //            }
    //        };
    //    }

    private File getFile(Long fileId) throws SQLException, IOException {
        MultiPartBlob mpb = dao.getFileBlob(fileId);

        File f = File.createTempFile("datafile", null);
        InputStream is = mpb.readUncompressedContents();
        FileOutputStream fos = FileUtils.openOutputStream(f);
        IOUtils.copy(is, fos);
        IOUtils.closeQuietly(is);
        IOUtils.closeQuietly(fos);

        return f;
    }

    private SingleConnectionHibernateHelper createHibernateHelper(Connection connection) {
        SingleConnectionHibernateHelper hibernateHelper = (SingleConnectionHibernateHelper) injector
                .getInstance(CaArrayHibernateHelper.class);

        hibernateHelper.initialize(connection);

        return hibernateHelper;
    }

    @SuppressWarnings("unchecked")
    private Set<DataFileHandler> getHandlers() {
        return (Set<DataFileHandler>) injector
                .getInstance(Key.get(TypeLiteral.get(Types.setOf(DataFileHandler.class))));
    }
}