Skip to content

Commit ac7c7f3

Browse files
committed
Fix critical bugs: NPE safety, resource leak, busy-wait
- GraphMatching: null-safe getAtomByID(), guard removeAtom(null) calls - Reactor: use "-1".equalsIgnoreCase(atom.getID()) pattern for null safety across all atom ID comparisons (lines 416-910) - Reactor: null-safe getContainerAtomByID() - CallableAtomMappingTool: replace busy-wait spin loop with awaitTermination - Annotator: fix resource leak in writeSimilarityMatrix() using try-with-resources (was leaking FileWriter on exceptions) - Helper: fix corrupted Javadoc in displayBlankLines() All 40 tests pass. Author: Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
1 parent 05c40ea commit ac7c7f3

5 files changed

Lines changed: 49 additions & 70 deletions

File tree

src/main/java/uk/ac/ebi/aamtool/Annotator.java

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -186,58 +186,33 @@ private static void writeSimilarityMatrix(List<SimilarityResult> results, String
186186
File rcMatrix = new File(rootPath, jobID + "_Reaction_Centre" + ".mat");
187187
File stMatrix = new File(rootPath, jobID + "_Structure_Similarity" + ".mat");
188188

189-
FileWriter writerBC = new FileWriter(bcMatrix);
190-
BufferedWriter bufferedWriterBC = new BufferedWriter(writerBC);
191-
192-
FileWriter writerRC = new FileWriter(rcMatrix);
193-
BufferedWriter bufferedWriterRC = new BufferedWriter(writerRC);
194-
195-
FileWriter writerST = new FileWriter(stMatrix);
196-
BufferedWriter bufferedWriterST = new BufferedWriter(writerST);
197-
198-
bufferedWriterBC.newLine();
199-
try {
189+
try (BufferedWriter bufferedWriterBC = new BufferedWriter(new FileWriter(bcMatrix))) {
190+
bufferedWriterBC.newLine();
200191
for (SimilarityResult s : results) {
201-
if (s.getSimilarityReactions().containsKey("BC")) {
202-
bufferedWriterBC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + s.getSimilarityReactions().get("BC"));
203-
bufferedWriterBC.newLine();
204-
} else {
205-
bufferedWriterBC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + "NA");
206-
bufferedWriterBC.newLine();
207-
}
192+
String val = s.getSimilarityReactions().containsKey("BC")
193+
? s.getSimilarityReactions().get("BC").toString() : "NA";
194+
bufferedWriterBC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + val);
195+
bufferedWriterBC.newLine();
208196
}
209-
} finally {
210-
bufferedWriterBC.close();
211197
}
212198

213-
try {
199+
try (BufferedWriter bufferedWriterRC = new BufferedWriter(new FileWriter(rcMatrix))) {
214200
for (SimilarityResult s : results) {
215-
if (s.getSimilarityReactions().containsKey("RC")) {
216-
bufferedWriterRC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + s.getSimilarityReactions().get("RC"));
217-
bufferedWriterRC.newLine();
218-
} else {
219-
bufferedWriterRC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + "NA");
220-
bufferedWriterRC.newLine();
221-
}
201+
String val = s.getSimilarityReactions().containsKey("RC")
202+
? s.getSimilarityReactions().get("RC").toString() : "NA";
203+
bufferedWriterRC.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + val);
204+
bufferedWriterRC.newLine();
222205
}
223-
} finally {
224-
bufferedWriterRC.close();
225206
}
226207

227-
try {
208+
try (BufferedWriter bufferedWriterST = new BufferedWriter(new FileWriter(stMatrix))) {
228209
for (SimilarityResult s : results) {
229-
if (s.getSimilarityReactions().containsKey("ST")) {
230-
bufferedWriterST.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + s.getSimilarityReactions().get("ST"));
231-
bufferedWriterST.newLine();
232-
} else {
233-
bufferedWriterST.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + "NA");
234-
bufferedWriterST.newLine();
235-
}
210+
String val = s.getSimilarityReactions().containsKey("ST")
211+
? s.getSimilarityReactions().get("ST").toString() : "NA";
212+
bufferedWriterST.write("\"" + s.getQuery() + "\"" + TAB + "\"" + s.getTarget() + "\"" + TAB + val);
213+
bufferedWriterST.newLine();
236214
}
237-
} finally {
238-
bufferedWriterST.close();
239215
}
240-
241216
}
242217

243218
private void printRPAIRPatternAsText(MappingSolution s, StringBuilder sb) throws CloneNotSupportedException {

src/main/java/uk/ac/ebi/aamtool/Helper.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,10 @@ protected static void getHeader() {
8080
}
8181

8282
/**
83-
* WreactionWithLayoutite the preactionWithLayoutovided
84-
* numbereactionWithLayout of blank lineheaderString to the
85-
* preactionWithLayoutovided OutputStreactionWithLayouteam.
83+
* Write the provided number of blank lines to the provided OutputStream.
8684
*
87-
* @param numberBlankLines NumbereactionWithLayout of blank lineheaderString
88-
* to wreactionWithLayoutite.
89-
* @param out OutputStreactionWithLayouteam to which to
90-
* wreactionWithLayoutite the blank lineheaderString.
85+
* @param numberBlankLines Number of blank lines to write.
86+
* @param out OutputStream to which to write the blank lines.
9187
*/
9288
protected static void displayBlankLines(final int numberBlankLines, final OutputStream out) {
9389
try {

src/main/java/uk/ac/ebi/reactionblast/mapping/CallableAtomMappingTool.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.concurrent.ExecutorCompletionService;
3636
import java.util.concurrent.ExecutorService;
3737
import java.util.concurrent.Executors;
38+
import java.util.concurrent.TimeUnit;
3839

3940
import org.openscience.cdk.interfaces.IReaction;
4041
import org.openscience.cdk.tools.ILoggingTool;
@@ -201,12 +202,7 @@ private synchronized void generateAtomAtomMapping(
201202
putSolution(chosen.getAlgorithm(), chosen);
202203
}
203204
executor.shutdown();
204-
/*
205-
Wait until all threads are finish
206-
*
207-
*/
208-
while (!executor.isTerminated()) {
209-
}
205+
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
210206
LOGGER.debug("======DONE CallableAtomMappingTool=======");
211207
gc();
212208
} catch (InterruptedException | ExecutionException e) {

src/main/java/uk/ac/ebi/reactionblast/mapping/Reactor.java

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ private synchronized IReaction getMapping(IReaction coreMappedReaction) throws I
413413
IAtomContainer eMolecule = mappedReaction.getReactants().getAtomContainer(eMol);
414414
for (int eAtom = 0; eAtom < eMolecule.getAtomCount(); eAtom++) {
415415
IAtom atom = mappedReaction.getReactants().getAtomContainer(eMol).getAtom(eAtom);
416-
if (!atom.getSymbol().equalsIgnoreCase("H") && atom.getID().equalsIgnoreCase("-1")) {
416+
if (!atom.getSymbol().equalsIgnoreCase("H") && "-1".equalsIgnoreCase(atom.getID())) {
417417
String atomLabel = Integer.toString(counter);
418418
atom.setID(atomLabel);
419419
atom.setFlag(MAPPED, false);
@@ -426,7 +426,7 @@ private synchronized IReaction getMapping(IReaction coreMappedReaction) throws I
426426
IAtomContainer pMolecule = mappedReaction.getProducts().getAtomContainer(pMol);
427427
for (int pAtom = 0; pAtom < pMolecule.getAtomCount(); pAtom++) {
428428
IAtom atom = mappedReaction.getProducts().getAtomContainer(pMol).getAtom(pAtom);
429-
if (!atom.getSymbol().equalsIgnoreCase("H") && atom.getID().equalsIgnoreCase("-1")) {
429+
if (!atom.getSymbol().equalsIgnoreCase("H") && "-1".equalsIgnoreCase(atom.getID())) {
430430
String atomLabel = Integer.toString(counter);
431431
atom.setID(atomLabel);
432432
atom.setFlag(MAPPED, false);
@@ -442,12 +442,12 @@ private synchronized IReaction getMapping(IReaction coreMappedReaction) throws I
442442
IAtomContainer eMolecule = mappedReaction.getReactants().getAtomContainer(eMol);
443443
for (int eAtom = 0; eAtom < eMolecule.getAtomCount(); eAtom++) {
444444
IAtom atom = mappedReaction.getReactants().getAtomContainer(eMol).getAtom(eAtom);
445-
if (!atom.getSymbol().equalsIgnoreCase("H") && !atom.getID().equalsIgnoreCase("-1")) {
445+
if (!atom.getSymbol().equalsIgnoreCase("H") && !"-1".equalsIgnoreCase(atom.getID())) {
446446
List<IAtom> eductConnAtoms = eMolecule.getConnectedAtomsList(atom);
447447
List<IAtom> productHAtoms = markHAroundCoreAtoms(atom.getID(), mappedReaction.getProducts());
448448
for (IAtom eAtomH : eductConnAtoms) {
449449
//Collect ummmarked H and map common ones
450-
if (eAtomH.getID().equalsIgnoreCase("-1") && eAtomH.getSymbol().equalsIgnoreCase("H")) {
450+
if ("-1".equalsIgnoreCase(eAtomH.getID()) && eAtomH.getSymbol().equalsIgnoreCase("H")) {
451451
if (!productHAtoms.isEmpty()) {
452452
String atomLabel = Integer.toString(counter);
453453
eAtomH.setID(atomLabel);
@@ -818,10 +818,10 @@ private synchronized List<IAtom> markHAroundCoreAtoms(String id, IAtomContainerS
818818
IAtomContainer pMolecule = molSet.getAtomContainer(pMol);
819819
for (int pAtom = 0; pAtom < pMolecule.getAtomCount(); pAtom++) {
820820
IAtom atom = molSet.getAtomContainer(pMol).getAtom(pAtom);
821-
if (!atom.getSymbol().equalsIgnoreCase("H") && !atom.getID().equalsIgnoreCase("-1")) {
822-
if (atom.getID().equalsIgnoreCase(id)) {
821+
if (!atom.getSymbol().equalsIgnoreCase("H") && !"-1".equalsIgnoreCase(atom.getID())) {
822+
if (id != null && id.equalsIgnoreCase(atom.getID())) {
823823
List<IAtom> conAtoms = pMolecule.getConnectedAtomsList(atom);
824-
conAtoms.stream().filter((atomH) -> (atomH.getID().equalsIgnoreCase("-1") && atomH.getSymbol().equalsIgnoreCase("H"))).forEach((atomH) -> {
824+
conAtoms.stream().filter((atomH) -> ("-1".equalsIgnoreCase(atomH.getID()) && atomH.getSymbol().equalsIgnoreCase("H"))).forEach((atomH) -> {
825825
list.add(atomH);
826826
});
827827
}
@@ -845,7 +845,7 @@ private synchronized List<IAtom> collectUnMappedSingleHAtoms(IAtomContainerSet m
845845
IAtom atom = molSet.getAtomContainer(index).getAtom(atomIndex);
846846
if (atom.getSymbol().equalsIgnoreCase("H")
847847
&& !atom.getFlag(MAPPED)
848-
&& atom.getID().equalsIgnoreCase("-1")) {
848+
&& "-1".equalsIgnoreCase(atom.getID())) {
849849
list.add(atom);
850850
}
851851
}
@@ -867,7 +867,7 @@ private synchronized List<IAtom> collectUnMappedHAtoms(IAtomContainerSet molSet)
867867
IAtom atom = molSet.getAtomContainer(index).getAtom(atomIndex);
868868
if (atom.getSymbol().equalsIgnoreCase("H")
869869
&& !atom.getFlag(MAPPED)
870-
&& atom.getID().equalsIgnoreCase("-1")) {
870+
&& "-1".equalsIgnoreCase(atom.getID())) {
871871
list.add(atom);
872872
}
873873
}
@@ -893,7 +893,7 @@ private synchronized int markUnMappedHAtoms(IReaction mappedReaction, int counte
893893
for (int eAtom = 0; eAtom < eMolecule.getAtomCount(); eAtom++) {
894894
IAtom atom = mappedReaction.getReactants().getAtomContainer(eMol).getAtom(eAtom);
895895
if (atom.getSymbol().equalsIgnoreCase("H") && !atom.getFlag(MAPPED)
896-
&& atom.getID().equalsIgnoreCase("-1")) {
896+
&& "-1".equalsIgnoreCase(atom.getID())) {
897897
String atomLabel = Integer.toString(localCounter);
898898
atom.setFlag(MAPPED, false);
899899
atom.setID(atomLabel);
@@ -907,7 +907,7 @@ private synchronized int markUnMappedHAtoms(IReaction mappedReaction, int counte
907907
for (int pAtom = 0; pAtom < pMolecule.getAtomCount(); pAtom++) {
908908
IAtom atom = mappedReaction.getProducts().getAtomContainer(pMol).getAtom(pAtom);
909909
if (atom.getSymbol().equalsIgnoreCase("H") && !atom.getFlag(MAPPED)
910-
&& atom.getID().equalsIgnoreCase("-1")) {
910+
&& "-1".equalsIgnoreCase(atom.getID())) {
911911
String atomLabel = Integer.toString(localCounter);
912912
atom.setID(atomLabel);
913913
atom.setFlag(MAPPED, false);
@@ -940,9 +940,12 @@ private synchronized void setReactionBlastMolMapping(MoleculeMoleculeMapping rea
940940
}
941941

942942
private synchronized IAtom getContainerAtomByID(IAtomContainerSet products, String mappingID) {
943+
if (mappingID == null) {
944+
return null;
945+
}
943946
for (IAtomContainer ac : products.atomContainers()) {
944947
for (IAtom atom : ac.atoms()) {
945-
if (atom.getID().equals(mappingID)) {
948+
if (mappingID.equals(atom.getID())) {
946949
return atom;
947950
}
948951
}

src/main/java/uk/ac/ebi/reactionblast/mapping/graph/GraphMatching.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,21 @@ public synchronized int removeMatchedAtomsAndUpdateAAM(IReaction reaction) {
162162
IMapping im = SilentChemObjectBuilder.getInstance().newInstance(IMapping.class, eAtom, pAtom);
163163
reaction.addMapping(im);
164164
}
165-
educt.removeAtom(eAtom);
166-
product.removeAtom(pAtom);
165+
if (eAtom != null) {
166+
educt.removeAtom(eAtom);
167+
}
168+
if (pAtom != null) {
169+
product.removeAtom(pAtom);
170+
}
167171
delta = fragmentCount;
168172
}
169173
}
170174

171175
for (IAtom atom : educt.atoms()) {
172176
IAtom matchedAtom = getAtomByID(matchedPart, atom.getID());
173-
matchedPart.removeAtom(matchedAtom);
177+
if (matchedAtom != null) {
178+
matchedPart.removeAtom(matchedAtom);
179+
}
174180
}
175181

176182
LOGGER.debug("After removing Mol Size E: " + educt.getAtomCount()
@@ -197,8 +203,11 @@ public synchronized int removeMatchedAtomsAndUpdateAAM(IReaction reaction) {
197203
}
198204

199205
private synchronized IAtom getAtomByID(IAtomContainer ac, String ID) {
206+
if (ID == null) {
207+
return null;
208+
}
200209
for (IAtom atom : ac.atoms()) {
201-
if (atom.getID().equalsIgnoreCase(ID)) {
210+
if (ID.equalsIgnoreCase(atom.getID())) {
202211
return atom;
203212
}
204213
}

0 commit comments

Comments
 (0)