From f11ec198d5d7d59854f1a831a40bdeb7eb4c2948 Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Thu, 26 Oct 2023 15:49:54 +0200 Subject: [PATCH] fix: concurrent modification of rdfServiceJena getGraphURIs --- .../rdfservice/impl/jena/RDFServiceJena.java | 51 +++++++++---------- .../impl/jena/RDFServiceJenaTest.java | 51 +++++++++++++++++++ 2 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJenaTest.java diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java index fef7b54f54..79e6e4496c 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJena.java @@ -7,25 +7,30 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; - import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper; +import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils; +import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceImpl; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; +import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.ResultSetIterators.ResultSetQuadsIterator; +import edu.cornell.mannlib.vitro.webapp.utils.sparql.ResultSetIterators.ResultSetTriplesIterator; +import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - -import org.apache.jena.query.QuerySolutionMap; -import org.apache.jena.query.ReadWrite; -import org.apache.jena.query.Syntax; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.riot.RDFDataMgr; -import org.apache.log4j.lf5.util.StreamUtils; - import org.apache.jena.graph.Triple; import org.apache.jena.query.Dataset; import org.apache.jena.query.Query; @@ -33,30 +38,20 @@ import org.apache.jena.query.QueryExecutionFactory; import org.apache.jena.query.QueryFactory; import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.query.ReadWrite; import org.apache.jena.query.ResultSet; import org.apache.jena.query.ResultSetFormatter; +import org.apache.jena.query.Syntax; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.riot.RDFDataMgr; import org.apache.jena.sdb.SDB; import org.apache.jena.shared.Lock; import org.apache.jena.sparql.core.Quad; - -import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; -import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper; -import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils; -import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset; -import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet; -import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange; -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; -import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; -import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; -import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceImpl; -import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; -import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.ResultSetIterators.ResultSetQuadsIterator; -import edu.cornell.mannlib.vitro.webapp.utils.sparql.ResultSetIterators.ResultSetTriplesIterator; -import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread; +import org.apache.log4j.lf5.util.StreamUtils; public abstract class RDFServiceJena extends RDFServiceImpl implements RDFService { @@ -66,7 +61,7 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic protected volatile boolean rebuildGraphURICache = true; protected volatile boolean isRebuildGraphURICacheRunning = false; - protected final List graphURIs = Collections.synchronizedList(new ArrayList<>()); + protected final List graphURIs = new CopyOnWriteArrayList(); @Override public abstract boolean changeSetUpdate(ChangeSet changeSet) throws RDFServiceException; diff --git a/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJenaTest.java b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJenaTest.java new file mode 100644 index 0000000000..f789ee919d --- /dev/null +++ b/api/src/test/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/jena/RDFServiceJenaTest.java @@ -0,0 +1,51 @@ +package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena; + +import java.util.Iterator; +import java.util.List; + +import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; +import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory; +import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.DatasetFactory; +import org.apache.jena.rdf.model.Model; +import org.junit.Test; + +public class RDFServiceJenaTest { + + @Test + public void getConcurrentGraphUrisTest() throws RDFServiceException, InterruptedException { + Dataset testDataSet = DatasetFactory.createGeneral(); + Model m1 = VitroModelFactory.createModel(); + testDataSet.addNamedModel("test:init1", m1); + Model m2 = VitroModelFactory.createModel(); + testDataSet.addNamedModel("test:init2", m2); + RDFServiceJena rdfService = new RDFServiceModel(testDataSet); + rdfService.getGraphURIs(); + long i = 0; + while (rdfService.rebuildGraphURICache) { + Thread.sleep(10); + i += 10; + if (i > 10000) { + throw new RuntimeException(); + } + } + List uris = rdfService.getGraphURIs(); + for (Iterator iterator = uris.iterator(); iterator.hasNext();) { + Model m = VitroModelFactory.createModel(); + testDataSet.addNamedModel("test" + i, m); + rdfService.rebuildGraphURICache = true; + rdfService.getGraphURIs(); + while (rdfService.rebuildGraphURICache) { + Thread.sleep(10); + i += 10; + if (i > 20000) { + throw new RuntimeException(); + } + } + iterator.next(); + i++; + } + } + +}