Skip to content

Commit 18aa4ad

Browse files
committed
Handle interrupted IO operations in loadModel() and loadModelFromStream()
1 parent 618d587 commit 18aa4ad

5 files changed

Lines changed: 101 additions & 113 deletions

File tree

src/ubic/basecode/ontology/jena/AbstractOntologyMemoryBackedService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import com.hp.hpl.jena.ontology.OntModel;
1818

19+
import java.io.IOException;
1920
import java.io.InputStream;
2021

2122
/**
@@ -31,7 +32,7 @@ protected boolean getProcessImport() {
3132
}
3233

3334
@Override
34-
protected OntModel loadModel() {
35+
protected OntModel loadModel() throws IOException {
3536
return OntologyLoader.loadMemoryModel( this.getOntologyUrl(), this.getCacheName(), this.getProcessImport() );
3637
}
3738

src/ubic/basecode/ontology/jena/AbstractOntologyService.java

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,13 @@
4646
import javax.annotation.Nullable;
4747
import java.io.IOException;
4848
import java.io.InputStream;
49+
import java.io.InterruptedIOException;
50+
import java.nio.channels.ClosedByInterruptException;
4951
import java.util.*;
5052
import java.util.concurrent.locks.Lock;
5153
import java.util.concurrent.locks.ReadWriteLock;
5254
import java.util.concurrent.locks.ReentrantReadWriteLock;
55+
import java.util.function.Predicate;
5356
import java.util.stream.Collectors;
5457

5558
import static ubic.basecode.ontology.jena.JenaUtils.where;
@@ -138,8 +141,15 @@ private void initialize( @Nullable InputStream stream, boolean forceLoad, boolea
138141
if ( checkIfInterrupted() )
139142
return;
140143

141-
model = stream != null ? loadModelFromStream( stream ) : loadModel(); // can take a while.
142-
assert model != null;
144+
try {
145+
model = stream != null ? loadModelFromStream( stream ) : loadModel(); // can take a while.
146+
} catch ( Exception e ) {
147+
if ( isCausedByInterrupt( e ) ) {
148+
return;
149+
} else {
150+
throw new RuntimeException( String.format( "Failed to load ontology model for %s.", this ), e );
151+
}
152+
}
143153

144154
// retrieving restrictions is lengthy
145155
if ( checkIfInterrupted() )
@@ -160,8 +170,16 @@ private void initialize( @Nullable InputStream stream, boolean forceLoad, boolea
160170
boolean indexExists = OntologyIndexer.getSubjectIndex( cacheName ) != null;
161171
boolean forceReindexing = forceLoad && forceIndexing;
162172
// indexing is slow, don't do it if we don't have to.
163-
index = OntologyIndexer.indexOntology( cacheName, model,
164-
forceReindexing || changed || !indexExists );
173+
try {
174+
index = OntologyIndexer.indexOntology( cacheName, model,
175+
forceReindexing || changed || !indexExists );
176+
} catch ( Exception e ) {
177+
if ( isCausedByInterrupt( e ) ) {
178+
return;
179+
} else {
180+
throw new RuntimeException( String.format( "Failed to generate index for %s.", this ), e );
181+
}
182+
}
165183
} else {
166184
index = null;
167185
}
@@ -202,6 +220,21 @@ private boolean checkIfInterrupted() {
202220
return false;
203221
}
204222

223+
private static boolean isCausedByInterrupt( Exception e ) {
224+
return hasCauseMatching( e, cause -> ( ( cause instanceof ParseException ) && ( ( ParseException ) cause ).getErrorNumber() == ARPErrorNumbers.ERR_INTERRUPTED ) ) ||
225+
hasCause( e, InterruptedException.class ) ||
226+
hasCause( e, InterruptedIOException.class ) ||
227+
hasCause( e, ClosedByInterruptException.class );
228+
}
229+
230+
private static boolean hasCause( Throwable t, Class<? extends Throwable> clazz ) {
231+
return hasCauseMatching( t, clazz::isInstance );
232+
}
233+
234+
private static boolean hasCauseMatching( Throwable t, Predicate<Throwable> predicate ) {
235+
return predicate.test( t ) || ( t.getCause() != null && hasCauseMatching( t.getCause(), predicate ) );
236+
}
237+
205238
/**
206239
* Do not do this except before re-indexing.
207240
*/
@@ -211,7 +244,8 @@ public void closeIndex() {
211244
}
212245

213246
@Override
214-
public Collection<OntologyIndividual> findIndividuals( String search, boolean keepObsoletes ) throws OntologySearchException {
247+
public Collection<OntologyIndividual> findIndividuals( String search, boolean keepObsoletes ) throws
248+
OntologySearchException {
215249
Lock lock = rwLock.readLock();
216250
try {
217251
lock.lock();
@@ -233,7 +267,8 @@ public Collection<OntologyIndividual> findIndividuals( String search, boolean ke
233267
}
234268

235269
@Override
236-
public Collection<OntologyResource> findResources( String searchString, boolean keepObsoletes ) throws OntologySearchException {
270+
public Collection<OntologyResource> findResources( String searchString, boolean keepObsoletes ) throws
271+
OntologySearchException {
237272
Lock lock = rwLock.readLock();
238273
try {
239274
lock.lock();
@@ -393,7 +428,8 @@ public Collection<OntologyIndividual> getTermIndividuals( String uri ) {
393428
}
394429

395430
@Override
396-
public Set<OntologyTerm> getParents( Collection<OntologyTerm> terms, boolean direct, boolean includeAdditionalProperties, boolean keepObsoletes ) {
431+
public Set<OntologyTerm> getParents( Collection<OntologyTerm> terms, boolean direct,
432+
boolean includeAdditionalProperties, boolean keepObsoletes ) {
397433
Lock lock = rwLock.readLock();
398434
try {
399435
lock.lock();
@@ -411,7 +447,8 @@ public Set<OntologyTerm> getParents( Collection<OntologyTerm> terms, boolean dir
411447
}
412448

413449
@Override
414-
public Set<OntologyTerm> getChildren( Collection<OntologyTerm> terms, boolean direct, boolean includeAdditionalProperties, boolean keepObsoletes ) {
450+
public Set<OntologyTerm> getChildren( Collection<OntologyTerm> terms, boolean direct,
451+
boolean includeAdditionalProperties, boolean keepObsoletes ) {
415452
Lock lock = rwLock.readLock();
416453
try {
417454
lock.lock();
@@ -463,13 +500,8 @@ public synchronized void startInitializationThread( boolean forceLoad, boolean f
463500
initializationThread = new Thread( () -> {
464501
try {
465502
this.initialize( forceLoad, forceIndexing );
466-
} catch ( JenaException e ) {
467-
if ( !( e.getCause() instanceof ParseException ) || ( ( ParseException ) e.getCause() ).getErrorNumber() != ARPErrorNumbers.ERR_INTERRUPTED ) {
468-
throw e;
469-
}
470503
} catch ( Exception e ) {
471-
log.error( e.getMessage(), e );
472-
this.isInitialized = false;
504+
log.error( "Initialization for %s failed.", e );
473505
}
474506
}, getOntologyName() + "_load_thread_" + RandomStringUtils.randomAlphanumeric( 5 ) );
475507
// To prevent VM from waiting on this thread to shut down (if shutting down).
@@ -521,13 +553,13 @@ public void waitForInitializationThread() throws InterruptedException {
521553
* Delegates the call as to load the model into memory or leave it on disk. Simply delegates to either
522554
* OntologyLoader.loadMemoryModel( url ); OR OntologyLoader.loadPersistentModel( url, spec );
523555
*/
524-
protected abstract OntModel loadModel();
556+
protected abstract OntModel loadModel() throws JenaException, IOException;
525557

526558

527559
/**
528560
* Load a model from a given input stream.
529561
*/
530-
protected abstract OntModel loadModelFromStream( InputStream stream );
562+
protected abstract OntModel loadModelFromStream( InputStream stream ) throws JenaException, IOException;
531563

532564
/**
533565
* A name for caching this ontology, or null to disable caching.
@@ -555,6 +587,9 @@ public void index( boolean force ) {
555587
return;
556588
}
557589
index = OntologyIndexer.indexOntology( getCacheName(), model, force );
590+
} catch ( IOException e ) {
591+
log.error( "Failed to generate index for {}.", this, e );
592+
return;
558593
} finally {
559594
lock.unlock();
560595
}

src/ubic/basecode/ontology/jena/OntologyLoader.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.net.HttpURLConnection;
3737
import java.net.URL;
3838
import java.net.URLConnection;
39+
import java.nio.channels.ClosedByInterruptException;
3940
import java.nio.file.Files;
4041
import java.nio.file.Paths;
4142
import java.nio.file.StandardCopyOption;
@@ -48,18 +49,17 @@
4849
public class OntologyLoader {
4950

5051
private static final Logger log = LoggerFactory.getLogger( OntologyLoader.class );
51-
private static final int MAX_CONNECTION_TRIES = 3;
5252
private static final String OLD_CACHE_SUFFIX = ".old";
5353
private static final String TMP_CACHE_SUFFIX = ".tmp";
5454

55-
public static OntModel loadMemoryModel( InputStream is, String url ) {
55+
public static OntModel loadMemoryModel( InputStream is, String url ) throws JenaException {
5656
return loadMemoryModel( is, url, true );
5757
}
5858

5959
/**
6060
* Load an ontology into memory. Use this type of model when fast access is critical and memory is available.
6161
*/
62-
public static OntModel loadMemoryModel( InputStream is, String url, boolean processImports ) {
62+
public static OntModel loadMemoryModel( InputStream is, String url, boolean processImports ) throws JenaException {
6363
OntModel model = getMemoryModel( url, processImports );
6464
model.read( is, null );
6565
return model;
@@ -70,11 +70,11 @@ public static OntModel loadMemoryModel( InputStream is, String url, boolean proc
7070
*
7171
* @see #loadMemoryModel(String, String, boolean)
7272
*/
73-
public static OntModel loadMemoryModel( String url ) {
73+
public static OntModel loadMemoryModel( String url ) throws IOException {
7474
return loadMemoryModel( url, null, true );
7575
}
7676

77-
public static OntModel loadMemoryModel( String url, @Nullable String cacheName ) {
77+
public static OntModel loadMemoryModel( String url, @Nullable String cacheName ) throws JenaException, IOException {
7878
return loadMemoryModel( url, cacheName, true );
7979
}
8080

@@ -87,7 +87,7 @@ public static OntModel loadMemoryModel( String url, @Nullable String cacheName )
8787
* @param url a URL where the OWL file is stored
8888
* @param cacheName unique name of this ontology, will be used to load from disk in case of failed url connection
8989
*/
90-
public static OntModel loadMemoryModel( String url, @Nullable String cacheName, boolean processImports ) {
90+
public static OntModel loadMemoryModel( String url, @Nullable String cacheName, boolean processImports ) throws JenaException, IOException {
9191
StopWatch timer = new StopWatch();
9292
timer.start();
9393
OntModel model = getMemoryModel( url, processImports );
@@ -113,6 +113,8 @@ public static OntModel loadMemoryModel( String url, @Nullable String cacheName,
113113
}
114114
log.info( "Loading ontology model for " + url + " took " + timer.getTime() + "ms" );
115115
}
116+
} catch ( ClosedByInterruptException e ) {
117+
throw e;
116118
} catch ( IOException e ) {
117119
log.error( "Failed to load ontology model for " + url + ", will attempt to load from disk.", e );
118120
attemptToLoadFromDisk = true;
@@ -137,9 +139,6 @@ public static OntModel loadMemoryModel( String url, @Nullable String cacheName,
137139
FileUtils.createParentDirectories( oldFile );
138140
Files.copy( f.toPath(), oldFile.toPath(), StandardCopyOption.REPLACE_EXISTING );
139141
log.info( "Load model from disk: " + timer.getTime() + "ms" );
140-
} catch ( IOException e ) {
141-
throw new RuntimeException(
142-
"Ontology failed load from URL (" + url + ") and disk cache: " + cacheName );
143142
}
144143
} else {
145144
throw new RuntimeException(

0 commit comments

Comments
 (0)