Skip to content

Commit

Permalink
re-enable reading bam files from pipes (#1085)
Browse files Browse the repository at this point in the history
* re-enable reading bam files from pipes
fixing an issue that prevented reading a bam file from an unseekable file, ex: a unix pipe
fixes #1083 which was introduced by #1077

this only fixes the case where the bam is being opened as file, a similar issue exists for Paths (#1084)
support for reading pipes as paths has never worked
  • Loading branch information
lbergelson authored Feb 8, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 5c8de55 commit 2b8919b
Showing 3 changed files with 79 additions and 14 deletions.
30 changes: 21 additions & 9 deletions src/main/java/htsjdk/samtools/SamInputResource.java
Original file line number Diff line number Diff line change
@@ -33,17 +33,12 @@
import htsjdk.samtools.util.Lazy;
import htsjdk.samtools.util.RuntimeIOException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;
import java.util.function.Supplier;

@@ -90,7 +85,9 @@ public String toString() {
}

/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final File file) { return new SamInputResource(new FileInputResource(file)); }
public static SamInputResource of(final File file) {
return new SamInputResource(new FileInputResource(file));
}

/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final Path path) {
@@ -269,12 +266,27 @@ public URL asUrl() {

@Override
public SeekableStream asUnbufferedSeekableStream() {
return lazySeekableStream.get();
//if the file doesn't exist, the try to open the stream anyway because users might be expecting the exception
//if it not a regular file than we won't be able to seek on it, so return null
if (!fileResource.exists() || fileResource.isFile()) {
return lazySeekableStream.get();
} else {
return null;
}
}

@Override
public InputStream asUnbufferedInputStream() {
return asUnbufferedSeekableStream();
final SeekableStream seekableStream = asUnbufferedSeekableStream();
if (seekableStream != null) {
return seekableStream;
} else {
try {
return new FileInputStream(fileResource);
} catch (FileNotFoundException e) {
throw new RuntimeIOException(e);
}
}
}

@Override
63 changes: 58 additions & 5 deletions src/test/java/htsjdk/samtools/SamReaderFactoryTest.java
Original file line number Diff line number Diff line change
@@ -7,11 +7,6 @@
import htsjdk.samtools.seekablestream.SeekableHTTPStream;
import htsjdk.samtools.seekablestream.SeekableStreamFactory;
import htsjdk.samtools.util.*;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Paths;
import java.util.function.Function;
import htsjdk.samtools.util.zip.InflaterFactory;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
@@ -23,12 +18,17 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.zip.Inflater;

public class SamReaderFactoryTest extends HtsjdkTest {
@@ -536,4 +536,57 @@ public void testSamReaderFromMalformedSeekableStream() throws IOException {
countRecords(reader);
}

@Test(singleThreaded = true, groups="unix")
public void testWriteAndReadFromPipe() throws IOException, InterruptedException, ExecutionException, TimeoutException {
final File fifo = File.createTempFile("fifo", "");
Assert.assertTrue(fifo.delete());
fifo.deleteOnExit();
final Process exec = Runtime.getRuntime().exec(new String[]{"mkfifo", fifo.getAbsolutePath()});
exec.waitFor(1, TimeUnit.MINUTES);
Assert.assertEquals(exec.exitValue(), 0, "mkfifo failed with exit code " + 0);

ExecutorService executor = null;
try {
executor = Executors.newFixedThreadPool(2);
final File input = new File(TEST_DATA_DIR, "example.bam");
final Future<Integer> writing = executor.submit(writeToPipe(fifo, input));
final Future<Integer> reading = executor.submit(readFromPipe(fifo));
Assert.assertEquals(writing.get(1, TimeUnit.MINUTES), reading.get(1, TimeUnit.MINUTES));
} finally {
if (executor != null) {
executor.shutdownNow();
}
}
}

private static Callable<Integer> readFromPipe(File fifo) {
return () -> {
try (final SamReader reader = SamReaderFactory.makeDefault().open(fifo)) {
return (int)reader.iterator().stream().count();
} catch (Exception e) {
Assert.fail("failed during reading from pipe", e);
}
throw new RuntimeException("Shouldn't actually reach here but the compiler was confused");
};
}

private static Callable<Integer> writeToPipe(File fifo, File input) {
return () -> {
int written = 0;
try {
try (final SamReader reader = SamReaderFactory.makeDefault().open(input);
final SAMFileWriter writer = new SAMFileWriterFactory().setCreateIndex(false)
.setCreateMd5File(false)
.makeBAMWriter(reader.getFileHeader(), true, fifo)) {
for (SAMRecord read : reader) {
writer.addAlignment(read);
written++;
}
}
} catch (final Exception e) {
Assert.fail("Failed during writing to pipe", e);
}
return written;
};
}
}
Binary file added src/test/resources/htsjdk/samtools/example.bam
Binary file not shown.

0 comments on commit 2b8919b

Please sign in to comment.