Repository navigation
Fix #703: Recover from empty structure files in PDB_CACHE_DIR #774
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
Changes from 6 commits
5c1a335
4785d57
c3cec91
b207d34
6ea9ae7
62fb2a4
f5b325f
13f267c
c1c3c30
0c10a2e
08ab3e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,6 +127,9 @@ public static enum FetchBehavior { | |
|
|
||
| protected static final String lineSplit = System.getProperty("file.separator"); | ||
|
|
||
| /** Minimum size for a valid structure file (CIF or PDB), in bytes */ | ||
| public static final long MIN_PDB_FILE_SIZE = 40; // Empty gzip files are 20bytes. Add a few more for buffer. | ||
|
|
||
| private File path; | ||
| private List<String> extensions; | ||
|
|
||
|
|
@@ -687,6 +690,14 @@ public File getLocalFile(String pdbId) { | |
| for(String ex : getExtensions() ){ | ||
| File f = new File(searchdir,prefix + pdbId.toLowerCase() + ex) ; | ||
| if ( f.exists()) { | ||
| // delete files that are too short to have contents | ||
| if( f.length() < MIN_PDB_FILE_SIZE ) { | ||
| boolean success = f.delete(); | ||
| if( ! success) { | ||
| return null; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this rather throw an exception (perhaps IOException)? I don't see how can this null be handled if file can't be deleted.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switched to |
||
| } | ||
| assert(!f.exists()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather log a warning here. Assert is going to be ignored in normal situations
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's intended. If the delete was successful then the file will not exist. This is just a sanity check during development to make sure delete() wasn't asynchronous or something. |
||
| } | ||
| return f; | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,9 +68,8 @@ public static ChemComp getChemComp(String recordName){ | |
| * again. Note that this change can have unexpected behavior of | ||
| * code executed afterwards. | ||
| * <p> | ||
| * Changing the provider does not reset the cache, so Chemical | ||
| * Component definitions already downloaded from previous providers | ||
| * will be used. To reset the cache see {@link #getCache()). | ||
| * Changing the provider also resets the cache, so any groups | ||
| * previously accessed will be re-downloaded and reread. | ||
| * | ||
| * @param provider | ||
| */ | ||
|
|
@@ -84,6 +83,13 @@ public static void setChemCompProvider(ChemCompProvider provider) { | |
| public static ChemCompProvider getChemCompProvider(){ | ||
| return chemCompProvider; | ||
| } | ||
|
|
||
| /** | ||
| * Force the cache to be reset. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This clears the memory cache, right? Could you add that to javadoc, it's an important detail since there is both memory and file cache
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only memory. Documented. |
||
| */ | ||
| public static void clearCache() { | ||
| cache.clear(); | ||
| } | ||
|
|
||
| public static Group getGroupFromChemCompDictionary(String recordName) { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,6 +42,7 @@ | |
| import org.biojava.nbio.core.util.InputStreamProvider; | ||
| import org.biojava.nbio.structure.align.util.HTTPConnectionTools; | ||
| import org.biojava.nbio.structure.align.util.UserConfiguration; | ||
| import org.biojava.nbio.structure.io.LocalPDBDirectory; | ||
| import org.biojava.nbio.structure.io.mmcif.model.ChemComp; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
@@ -85,6 +86,8 @@ public class DownloadChemCompProvider implements ChemCompProvider { | |
| protectedIDs.add("AUX"); | ||
| protectedIDs.add("NUL"); | ||
| } | ||
|
|
||
| private static ChemCompProvider fallback = null; // Fallback provider if the download fails | ||
|
|
||
| /** by default we will download only some of the files. User has to request that all files should be downloaded... | ||
| * | ||
|
|
@@ -272,7 +275,10 @@ public ChemComp getChemComp(String recordName) { | |
|
|
||
| ChemComp chemComp = dict.getChemComp(recordName); | ||
|
|
||
| return chemComp; | ||
| // May be null if the file was corrupt. Fall back on ReducedChemCompProvider in that case | ||
| if(chemComp != null) { | ||
| return chemComp; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a small change in behavior. If we can't read the downloaded chemcomp file, we now return a stub chemcomp rather than NULL. Previously the stub was returned for corrupt GZIP files (which threw an error) but not for malformed cif contents (which just returned null). |
||
| } | ||
|
|
||
| } catch (IOException e) { | ||
|
|
||
|
|
@@ -296,9 +302,11 @@ public ChemComp getChemComp(String recordName) { | |
|
|
||
| // see https://github.com/biojava/biojava/issues/315 | ||
| // probably a network error happened. Try to use the ReducedChemCOmpProvider | ||
| ReducedChemCompProvider reduced = new ReducedChemCompProvider(); | ||
| if( fallback == null) { | ||
| fallback = new ReducedChemCompProvider(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. creating a ReducedChemCompProvider is cheap (and idempotent), but let's cache it anyways. |
||
| } | ||
|
|
||
| return reduced.getChemComp(recordName); | ||
| return fallback.getChemComp(recordName); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a warning logged when the fallback is used? That'd be important, so that users are aware and can investigate if there's something wrong in their code or file system
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. |
||
|
|
||
| } | ||
|
|
||
|
|
@@ -337,6 +345,12 @@ private static boolean fileExists(String recordName){ | |
|
|
||
| File f = new File(fileName); | ||
|
|
||
| // delete files that are too short to have contents | ||
| if( f.length() < LocalPDBDirectory.MIN_PDB_FILE_SIZE ) { | ||
| f.delete(); | ||
| return false; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the file fails to be deleted, this won't work as expected. I think the fail to delete case should throw an IOException
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't check the status because the delete isn't really necessary here. If this method returns false then we re-download the file and move it on top of the old one. I just added the delete defensively. |
||
| } | ||
|
|
||
| return f.exists(); | ||
|
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -328,7 +328,13 @@ public void newAtomSite(AtomSite atom) { | |
|
|
||
| String recordName = atom.getGroup_PDB(); | ||
| String residueNumberS = atom.getAuth_seq_id(); | ||
| Integer residueNrInt = Integer.parseInt(residueNumberS); | ||
| Integer residueNrInt; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you handle this in a separate pull request? as per discussion in #775 |
||
| if(residueNumberS != null) { | ||
| residueNrInt = Integer.parseInt(residueNumberS); | ||
| } else { | ||
| String label_seq_id = atom.getLabel_seq_id(); | ||
| residueNrInt = Integer.parseInt(label_seq_id); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should discuss this further. I added it because the atp.cif.gz file I added (from pymol) has label_seq_id but not auth_seq_id. It seems reasonable to me to fall back on the sequential numbering if the authors don't specify a custom numbering. Note that
Ideally BioJava would use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's another potential issue here, which is that the seq_id is stored as a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I started #775 to continue this discussion. |
||
| } | ||
|
|
||
| // the 3-letter name of the group: | ||
| String groupCode3 = atom.getLabel_comp_id(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -212,6 +212,9 @@ public void parse(BufferedReader buf) | |
|
|
||
| // the first line is a data_PDBCODE line, test if this looks like a mmcif file | ||
| line = buf.readLine(); | ||
| while( line != null && (line.isEmpty() || line.startsWith(COMMENT_CHAR))) { | ||
| line = buf.readLine(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice to handle comments in first line! Is an empty first line also valid CIF?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think blank lines are permitted anywhere in CIF, but I could be wrong. I try to write permissive parsers. It seems like it shouldn't break the spec so we might as well be robust to it either way. |
||
| if (line == null || !line.startsWith(MMCIF_TOP_HEADER)){ | ||
| logger.error("This does not look like a valid mmCIF file! The first line should start with 'data_', but is: '" + line+"'"); | ||
| triggerDocumentEnd(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,9 +21,6 @@ | |
| */ | ||
| package org.biojava.nbio.structure.io.util; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.io.File; | ||
| import java.io.FileInputStream; | ||
| import java.io.FileOutputStream; | ||
|
|
@@ -36,6 +33,15 @@ | |
| import java.nio.channels.Channels; | ||
| import java.nio.channels.FileChannel; | ||
| import java.nio.channels.ReadableByteChannel; | ||
| import java.nio.file.FileVisitResult; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.nio.file.Paths; | ||
| import java.nio.file.SimpleFileVisitor; | ||
| import java.nio.file.attribute.BasicFileAttributes; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| public class FileDownloadUtils { | ||
|
|
||
|
|
@@ -240,6 +246,41 @@ public static URLConnection prepareURLConnection(String url, int timeout) throws | |
| connection.setConnectTimeout(timeout); | ||
| return connection; | ||
| } | ||
|
|
||
| /** | ||
| * Recursively delete a folder & contents | ||
| * | ||
| * @param dir directory to delete | ||
| */ | ||
| public static void deleteDirectory(Path dir) throws IOException { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surprisingly this isn't an nio method and I couldn't find one in BioJava |
||
| if(dir == null || !Files.exists(dir)) | ||
| return; | ||
| Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { | ||
| @Override | ||
| public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
| Files.delete(file); | ||
| return FileVisitResult.CONTINUE; | ||
| } | ||
|
|
||
| @Override | ||
| public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { | ||
| if (e != null) { | ||
| throw e; | ||
| } | ||
| Files.delete(dir); | ||
| return FileVisitResult.CONTINUE; | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Recursively delete a folder & contents | ||
| * | ||
| * @param dir directory to delete | ||
| */ | ||
| public static void deleteDirectory(String dir) throws IOException { | ||
| deleteDirectory(Paths.get(dir)); | ||
| } | ||
|
|
||
|
|
||
| public static void main(String[] args) { | ||
| String url; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be "Downloading"? It isn't downloaded at this point yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed