Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Genome loading refactor to recover gracefully on startup errors #1553

Merged
merged 4 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading