/* * Copyright 2015-2016 OpenCB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opencb.opencga.storage.hadoop.variant.metadata; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.util.Bytes; import org.opencb.commons.datastore.core.ObjectMap; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryResult; import org.opencb.opencga.storage.core.metadata.StudyConfiguration; import org.opencb.opencga.storage.core.metadata.StudyConfigurationManager; import org.opencb.opencga.storage.hadoop.utils.HBaseManager; import org.opencb.opencga.storage.hadoop.variant.GenomeHelper; import org.opencb.opencga.storage.hadoop.variant.index.VariantTableDriver; import org.opencb.opencga.storage.hadoop.utils.HBaseLock; import java.io.IOException; import java.io.UncheckedIOException; import java.util.*; import java.util.concurrent.TimeoutException; /** * Created on 12/11/15. * * @author Jacobo Coll <jacobo167@gmail.com> */ public class HBaseStudyConfigurationManager extends StudyConfigurationManager { private final byte[] studiesRow; private final byte[] studiesSummaryColumn; private final Configuration configuration; private final ObjectMap options; private final GenomeHelper genomeHelper; private final ObjectMapper objectMapper; private final String tableName; private final HBaseLock lock; public HBaseStudyConfigurationManager(GenomeHelper helper, String tableName, Configuration configuration, ObjectMap options) throws IOException { super(options); this.configuration = Objects.requireNonNull(configuration); this.tableName = Objects.requireNonNull(tableName); this.options = options; this.genomeHelper = helper; this.objectMapper = new ObjectMapper(); this.studiesRow = genomeHelper.generateVariantRowKey(GenomeHelper.DEFAULT_METADATA_ROW_KEY, 0); this.studiesSummaryColumn = genomeHelper.generateVariantRowKey(GenomeHelper.DEFAULT_METADATA_ROW_KEY, 0); lock = new HBaseLock(getHBaseManager(), this.tableName, genomeHelper.getColumnFamily(), studiesRow); } public HBaseStudyConfigurationManager(String tableName, Configuration configuration, ObjectMap options) throws IOException { this(new GenomeHelper(configuration), tableName, configuration, options); } @Override protected QueryResult<StudyConfiguration> internalGetStudyConfiguration(int studyId, Long timeStamp, QueryOptions options) { logger.debug("Get StudyConfiguration " + studyId + " from DB " + tableName); return internalGetStudyConfiguration(getStudies(options).inverse().get(studyId), timeStamp, options); } @Override public long lockStudy(int studyId, long lockDuration, long timeout) throws InterruptedException, TimeoutException { try { VariantTableDriver.createVariantTableIfNeeded(genomeHelper, tableName, getConnection()); return lock.lock(Bytes.toBytes(studyId + "_LOCK"), lockDuration, timeout); } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public void unLockStudy(int studyId, long lockToken) { try { lock.unlock(Bytes.toBytes(studyId + "_LOCK"), lockToken); } catch (IOException e) { throw new UncheckedIOException(e); } } public GenomeHelper getGenomeHelper() { return this.genomeHelper; } protected HBaseManager getHBaseManager() { return this.getGenomeHelper().getHBaseManager(); } @Override protected QueryResult<StudyConfiguration> internalGetStudyConfiguration(String studyName, Long timeStamp, QueryOptions options) { long startTime = System.currentTimeMillis(); String error = null; List<StudyConfiguration> studyConfigurationList = Collections.emptyList(); logger.debug("Get StudyConfiguration {} from DB {}", studyName, tableName); if (StringUtils.isEmpty(studyName)) { return new QueryResult<>("", (int) (System.currentTimeMillis() - startTime), studyConfigurationList.size(), studyConfigurationList.size(), "", "", studyConfigurationList); } Get get = new Get(studiesRow); byte[] columnQualifier = Bytes.toBytes(studyName); get.addColumn(genomeHelper.getColumnFamily(), columnQualifier); if (timeStamp != null) { try { get.setTimeRange(timeStamp + 1, Long.MAX_VALUE); } catch (IOException e) { //This should not happen ever. throw new IllegalArgumentException(e); } } try { if (getHBaseManager().act(getConnection(), tableName, (table, admin) -> admin.tableExists(table.getName()))) { studyConfigurationList = getHBaseManager().act(getConnection(), tableName, table -> { Result result = table.get(get); if (result.isEmpty()) { return Collections.emptyList(); } else { byte[] value = result.getValue(genomeHelper.getColumnFamily(), columnQualifier); StudyConfiguration studyConfiguration = objectMapper.readValue(value, StudyConfiguration.class); return Collections.singletonList(studyConfiguration); } }); } } catch (IOException e) { throw new IllegalStateException("Problem checking Table " + tableName, e); } return new QueryResult<>("", (int) (System.currentTimeMillis() - startTime), studyConfigurationList.size(), studyConfigurationList.size(), "", error, studyConfigurationList); } @Override protected QueryResult internalUpdateStudyConfiguration(StudyConfiguration studyConfiguration, QueryOptions options) { long startTime = System.currentTimeMillis(); String error = ""; logger.info("Update StudyConfiguration {}", studyConfiguration.getStudyName()); updateStudiesSummary(studyConfiguration.getStudyName(), studyConfiguration.getStudyId(), options); byte[] columnQualifier = Bytes.toBytes(studyConfiguration.getStudyName()); studyConfiguration.getHeaders().clear(); // REMOVE: stored in Archive table try { getHBaseManager().act(tableName, table -> { byte[] bytes = objectMapper.writeValueAsBytes(studyConfiguration); Put put = new Put(studiesRow); put.addColumn(genomeHelper.getColumnFamily(), columnQualifier, studyConfiguration.getTimeStamp(), bytes); table.put(put); }); } catch (IOException e) { e.printStackTrace(); error = e.getMessage(); } return new QueryResult<>("", (int) (System.currentTimeMillis() - startTime), 0, 0, "", error, Collections.emptyList()); } @Override public BiMap<String, Integer> getStudies(QueryOptions options) { Get get = new Get(studiesRow); get.addColumn(genomeHelper.getColumnFamily(), studiesSummaryColumn); try { if (!getHBaseManager().act(tableName, (table, admin) -> admin.tableExists(table.getName()))) { logger.debug("Get StudyConfiguration summary TABLE_NO_EXISTS"); return HashBiMap.create(); } return getHBaseManager().act(tableName, table -> { Result result = table.get(get); if (result.isEmpty()) { logger.debug("Get StudyConfiguration summary EMPTY"); return HashBiMap.create(); } else { byte[] value = result.getValue(genomeHelper.getColumnFamily(), studiesSummaryColumn); Map<String, Integer> map = objectMapper.readValue(value, Map.class); logger.debug("Get StudyConfiguration summary {}", map); return HashBiMap.create(map); } }); } catch (IOException e) { e.printStackTrace(); logger.warn("Get StudyConfiguration summary ERROR"); return HashBiMap.create(); } } private void updateStudiesSummary(String study, Integer studyId, QueryOptions options) { BiMap<String, Integer> studiesSummary = getStudies(options); if (study.isEmpty()) { throw new IllegalStateException("Can't save an study with empty StudyName"); } if (studiesSummary.getOrDefault(study, Integer.MIN_VALUE).equals(studyId)) { //Nothing to update return; } else { studiesSummary.put(study, studyId); updateStudiesSummary(studiesSummary, options); } } private void updateStudiesSummary(BiMap<String, Integer> studies, QueryOptions options) { try { VariantTableDriver.createVariantTableIfNeeded(genomeHelper, tableName, getConnection()); try (Table table = getConnection().getTable(TableName.valueOf(tableName))) { byte[] bytes = objectMapper.writeValueAsBytes(studies); Put put = new Put(studiesRow); put.addColumn(genomeHelper.getColumnFamily(), studiesSummaryColumn, bytes); table.put(put); } catch (IOException e) { throw new UncheckedIOException(e); } } catch (IOException e) { throw new UncheckedIOException(e); } } public Connection getConnection() throws IOException { return this.getHBaseManager().getConnection(); } @Override public void close() throws IOException { try { this.getHBaseManager().close(); } catch (Exception e) { throw new IOException(e); } } }