Skip to content

Commit

Permalink
Genome loading refactor to recover gracefully on startup errors (#1553)
Browse files Browse the repository at this point in the history
* Genome loading refactor to recover gracefully on startup errors related to genome load
  • Loading branch information
jrobinso authored Sep 10, 2024
1 parent 5a9c3ec commit e8e38e1
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 204 deletions.
48 changes: 21 additions & 27 deletions src/main/java/org/broad/igv/feature/genome/Genome.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.broad.igv.track.TribbleFeatureSource;
import org.broad.igv.ucsc.Hub;
import org.broad.igv.ucsc.twobit.TwoBitSequence;
import org.broad.igv.util.ParsingUtils;
import org.broad.igv.util.ResourceLocator;
import org.broad.igv.util.liftover.Liftover;

Expand Down Expand Up @@ -142,8 +143,14 @@ public Genome(GenomeConfig config) throws IOException {
} else if (sequence != null && sequence.hasChromosomes()) {
chromosomeList = sequence.getChromosomes();
} else if (config.indexURL != null) {
FastaIndex index = new FastaIndex(config.indexURL);
chromosomeList = index.getChromosomes();
try {
// If chromosome info is not otherwise available try to parse the fasta index, if available. This
// situation can occur if a twoBitURL is defined but chromSizes is not.
FastaIndex index = new FastaIndex(config.indexURL);
chromosomeList = index.getChromosomes();
} catch (IOException e) {
log.error("Error loading fasta index", e);
}
}

// If list of chromosomes is specified use it for the whole genome view, and to prepopulate the
Expand Down Expand Up @@ -224,7 +231,7 @@ public Genome(GenomeConfig config) throws IOException {

/**
* Alternate constructor for defining a minimal genome, usually from parsing a chrom.sizes file. Used to
* create mock genomes for igvtools and testing.
* create mock genomes for igvtools and for testing.
*
* @param id
* @param chromosomes
Expand All @@ -242,6 +249,7 @@ public Genome(String id, List<Chromosome> chromosomes) {
chromosomeMap.put(chromosome.getName(), chromosome);
}
this.longChromosomeNames = computeLongChromosomeNames();
this.homeChromosome = this.longChromosomeNames.size() > 1 ? Globals.CHR_ALL : chromosomeNames.get(0);
this.chromAliasSource = (new ChromAliasDefaults(id, chromosomeNames));
}

Expand Down Expand Up @@ -623,18 +631,15 @@ public long getWGLength() {
}


// TODO A hack (obviously), we need to record a species in the genome definitions to support old style
// blat servers.
// Species mapping to support old style blat servers. This should not be needed for current IGV releases.
private static Map<String, String> ucscSpeciesMap;

private static synchronized String getSpeciesForID(String id) {
if (ucscSpeciesMap == null) {
ucscSpeciesMap = new HashMap<>();

InputStream is = null;
try (InputStream is = Genome.class.getResourceAsStream("speciesMapping.txt")) {

try {
is = Genome.class.getResourceAsStream("speciesMapping.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(is));

String nextLine;
Expand All @@ -649,14 +654,7 @@ private static synchronized String getSpeciesForID(String id) {
}
} catch (IOException e) {
log.error("Error reading species mapping table", e);
} finally {
if (is != null) try {
is.close();
} catch (IOException e) {
log.error("", e);
}
}

}

for (Map.Entry<String, String> entry : ucscSpeciesMap.entrySet()) {
Expand Down Expand Up @@ -685,18 +683,6 @@ public List<ResourceLocator> getAnnotationResources() {
return annotationResources;
}

/**
* Mock genome for unit tests
*/

private Genome(String id) {
this.id = id;
}

public boolean getShowWholeGenomeView() {
return showWholeGenomeView;
}

public Map<String, Liftover> getLiftoverMap() {
return liftoverMap;
}
Expand Down Expand Up @@ -809,4 +795,12 @@ public void setHub(Hub hub) {
this.hub = hub;
}

public synchronized static Genome nullGenome() {
if(nullGenome == null) {
nullGenome = new Genome("None", Arrays.asList(new Chromosome(0, "", 0)));
}
return nullGenome;
}

private static Genome nullGenome = null;
}
87 changes: 49 additions & 38 deletions src/main/java/org/broad/igv/feature/genome/GenomeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,48 +134,46 @@ public static File getGenomeFile(String genomePath) throws MalformedURLException
return archiveFile;
}

public void setCurrentGenome(Genome genome) {
if (genome != null) {
PreferencesManager.getPreferences().setLastGenome(genome.getId());
}
// Setter provided for unit tests
public void setCurrentGenomeForTest(Genome genome) {
this.currentGenome = genome;
if (genome != null) {
if (IGV.hasInstance()) {
IGV.getInstance().getSession().clearHistory();
FrameManager.getDefaultFrame().setChromosomeName(genome.getHomeChromosome(), true);
IGVEventBus.getInstance().post(new GenomeChangeEvent(genome));
}
}
}

/**
* Load a genome by ID, which might be a file path or URL
*
* @param genomeId - ID for an IGV hosted genome, or file path or url
* @return boolean flag indicating success
* @throws IOException
*/
public boolean loadGenomeById(String genomeId) throws IOException {

public void loadGenomeById(String genomeId) throws IOException {
final Genome currentGenome = getCurrentGenome();
if (currentGenome != null && genomeId.equals(currentGenome.getId())) {
return; // Already loaded
return false;
}

String genomePath = null;
String genomePath;
if (org.broad.igv.util.ParsingUtils.fileExists(genomeId)) {
genomePath = genomeId;
} else {
GenomeListItem item = genomeListManager.getGenomeListItem(genomeId);
if (item == null) {
MessageUtils.showMessage("Could not locate genome with ID: " + genomeId);
return;
return false;
} else {
genomePath = item.getPath();
}
}

loadGenome(genomePath); // monitor[0]);

return loadGenome(genomePath) != null; // monitor[0]);
}


/**
* The main load method -- loads a genome from a file or url path. Note this is a long running operation and
* should not be done on the Swing event thread as it will block the UI.
* <p>
* NOTE: The member 'currentGenome' is set here as a side effect.
*
* @param genomePath
* @return
Expand Down Expand Up @@ -214,26 +212,8 @@ public Genome loadGenome(String genomePath) throws IOException {
IGV.getInstance().resetSession(null);
}

GenomeListItem genomeListItem = new GenomeListItem(newGenome.getDisplayName(), genomePath, newGenome.getId());
final Set<String> serverGenomeIDs = genomeListManager.getServerGenomeIDs();

boolean userDefined = !serverGenomeIDs.contains(newGenome.getId());
genomeListManager.addGenomeItem(genomeListItem, userDefined);

setCurrentGenome(newGenome);

// hasInstance() test needed for unit tests
if (IGV.hasInstance()) {
IGV.getInstance().goToLocus(newGenome.getHomeChromosome()); // newGenome.getDefaultPos());
loadGenomeAnnotations(newGenome);
IGV.getInstance().resetFrames();
}
setCurrentGenome(genomePath, newGenome);

if (PreferencesManager.getPreferences().getAsBoolean(Constants.CIRC_VIEW_ENABLED) && CircularViewUtilities.ping()) {
CircularViewUtilities.changeGenome(newGenome);
}

// log.warn("Genome loaded. id= " + newGenome.getId());
return currentGenome;

} catch (SocketException e) {
Expand All @@ -246,6 +226,37 @@ public Genome loadGenome(String genomePath) throws IOException {
}
}

public void setCurrentGenome(String genomePath, Genome newGenome) {

GenomeListItem genomeListItem = new GenomeListItem(newGenome.getDisplayName(), genomePath, newGenome.getId());
final Set<String> serverGenomeIDs = genomeListManager.getServerGenomeIDs();

boolean userDefined = !serverGenomeIDs.contains(newGenome.getId());
genomeListManager.addGenomeItem(genomeListItem, userDefined);

this.currentGenome = newGenome;

// hasInstance() check to filters unit test
if (IGV.hasInstance()) {
IGV.getInstance().goToLocus(newGenome.getHomeChromosome()); // newGenome.getDefaultPos());
FrameManager.getDefaultFrame().setChromosomeName(newGenome.getHomeChromosome(), true);
loadGenomeAnnotations(newGenome);
IGV.getInstance().resetFrames();
IGV.getInstance().getSession().clearHistory();

if(newGenome != Genome.nullGenome()) {
// This should only occur on startup failure
PreferencesManager.getPreferences().setLastGenome(newGenome.getId());
}

if (PreferencesManager.getPreferences().getAsBoolean(Constants.CIRC_VIEW_ENABLED) && CircularViewUtilities.ping()) {
CircularViewUtilities.changeGenome(newGenome);
}

IGVEventBus.getInstance().post(new GenomeChangeEvent(newGenome));
}
}

/**
* Load and initialize the track objects from the genome's track resource locators. Does not add the tracks
* to the IGV instance.
Expand Down Expand Up @@ -458,7 +469,7 @@ private static void updateSequenceMapFile() {
public void refreshHostedGenome(String genomeId) {

Map<String, GenomeListItem> itemMap = GenomeListManager.getInstance().getServerGenomeMap();
if(itemMap.containsKey(genomeId)) {
if (itemMap.containsKey(genomeId)) {
downloadGenome(itemMap.get(genomeId), false);
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
import org.broad.igv.logging.Logger;
import org.broad.igv.track.TribbleFeatureSource;
import org.broad.igv.util.FileUtils;
import org.broad.igv.util.HttpUtils;
import org.broad.igv.util.ParsingUtils;
import org.broad.igv.util.ResourceLocator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand All @@ -35,17 +37,16 @@ public JsonGenomeLoader(String genomePath) {
@Override
public Genome loadGenome() throws IOException {

BufferedReader reader = null;

try {
reader = ParsingUtils.openBufferedReader(genomePath);
String jsonString = ParsingUtils.readContentsAsString(genomePath);
try (InputStream is = ParsingUtils.openInputStream(genomePath)){

String jsonString = ParsingUtils.readContentsFromStream(is);

if (jsonString.contains("chromosomeOrder")) {
jsonString = fixChromosomeOrder(jsonString);
}

GenomeConfig genomeConfig = GenomeConfig.fromJson (jsonString);
GenomeConfig genomeConfig = GenomeConfig.fromJson(jsonString);

fixPaths(genomeConfig);

Expand All @@ -68,8 +69,6 @@ public Genome loadGenome() throws IOException {

return genome;

} finally {
reader.close();
}
}

Expand Down Expand Up @@ -168,10 +167,9 @@ private void addToFeatureDB(List<ResourceLocator> locators, Genome genome) {
}

private String stripQuotes(String str) {
if(str.startsWith("\"")) {
if (str.startsWith("\"")) {
return str.substring(1, str.length() - 1); // Assume also ends with
}
else {
} else {
return str;
}
}
Expand Down
18 changes: 11 additions & 7 deletions src/main/java/org/broad/igv/session/SessionWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

package org.broad.igv.session;

import org.broad.igv.feature.genome.Genome;
import org.broad.igv.feature.genome.GenomeListItem;
import org.broad.igv.logging.*;
import org.broad.igv.feature.RegionOfInterest;
Expand Down Expand Up @@ -426,13 +427,16 @@ public Collection<ResourceLocator> getResourceLocatorSet() {
if (currentTrackFileLocators != null) {

// Filter data files that are included in genome annotations
List<ResourceLocator> genomeResources = GenomeManager.getInstance().getCurrentGenome().getAnnotationResources();
Set<String> absoluteGenomeAnnotationPaths = genomeResources == null ? Collections.emptySet() :
genomeResources.stream().map(rl -> rl.getPath()).collect(Collectors.toSet());

for (ResourceLocator locator : currentTrackFileLocators) {
if (!absoluteGenomeAnnotationPaths.contains(locator.getPath())) {
locators.add(locator);
final Genome currentGenome = GenomeManager.getInstance().getCurrentGenome();
if(currentGenome != null) {
List<ResourceLocator> genomeResources = currentGenome.getAnnotationResources();
Set<String> absoluteGenomeAnnotationPaths = genomeResources == null ? Collections.emptySet() :
genomeResources.stream().map(rl -> rl.getPath()).collect(Collectors.toSet());

for (ResourceLocator locator : currentTrackFileLocators) {
if (!absoluteGenomeAnnotationPaths.contains(locator.getPath())) {
locators.add(locator);
}
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/broad/igv/track/SequenceTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,12 @@ public void load(ReferenceFrame referenceFrame) {
end = Math.min(end + w / 2 + 2, chromosomeLength);

Genome genome = currentGenome;
String sequence = new String(genome.getSequence(chr, start, end));
byte [] seqBytes = genome.getSequence(chr, start, end);
if(seqBytes == null) {
return;

}
String sequence = new String(seqBytes);

int mod = start % 3;
int n1 = normalize3(3 - mod);
Expand Down
Loading

0 comments on commit e8e38e1

Please sign in to comment.