| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.rdfconnection; |
| |
| import java.util.Objects; |
| |
| import org.apache.jena.atlas.lib.InternalErrorException; |
| import org.apache.jena.graph.Graph; |
| import org.apache.jena.query.*; |
| import org.apache.jena.rdf.model.Model; |
| import org.apache.jena.rdf.model.ModelFactory; |
| import org.apache.jena.riot.Lang; |
| import org.apache.jena.riot.RDFDataMgr; |
| import org.apache.jena.riot.RDFLanguages; |
| import org.apache.jena.sparql.ARQException; |
| import org.apache.jena.sparql.core.DatasetGraph; |
| import org.apache.jena.sparql.core.DatasetGraphFactory; |
| import org.apache.jena.sparql.core.DatasetGraphReadOnly; |
| import org.apache.jena.sparql.graph.GraphReadOnly; |
| import org.apache.jena.system.Txn; |
| import org.apache.jena.update.UpdateExecution; |
| import org.apache.jena.update.UpdateExecutionBuilder; |
| import org.apache.jena.update.UpdateExecutionFactory; |
| import org.apache.jena.update.UpdateRequest; |
| |
| /** |
| * Implement of {@link RDFConnection} over a {@link Dataset} in the same JVM. |
| * <p> |
| * Multiple levels of {@link Isolation} are provided. The default {@code COPY} level makes a local |
| * {@link RDFConnection} that behaves like a remote connection. This should be the normal use in |
| * testing. |
| * <ul> |
| * <li>{@code COPY} – {@code Model}s and {@code Dataset}s are copied. |
| * This is most like a remote connection. |
| * <li>{@code READONLY} – Read-only wrappers are added but changes to |
| * the underlying model or dataset will be seen. |
| * <li>{@code NONE} (default) – Changes to the returned {@code Model}s or {@code Dataset}s act on the original object. |
| * </ul> |
| * @deprecated Use {@link RDFConnection#connect(Dataset)}. |
| */ |
| @Deprecated(forRemoval = true) |
| public class RDFConnectionLocal implements RDFConnection { |
| private ThreadLocal<Boolean> transactionActive = ThreadLocal.withInitial(()->false); |
| |
| private Dataset dataset; |
| private final Isolation isolation; |
| |
| public RDFConnectionLocal(Dataset dataset) { |
| this(dataset, Isolation.NONE); |
| } |
| |
| public RDFConnectionLocal(Dataset dataset, Isolation isolation) { |
| this.dataset = dataset; |
| this.isolation = isolation; |
| } |
| |
| @Override |
| public QueryExecution query(Query query) { |
| checkOpen(); |
| // There is no point doing this in a transaction because the QueryExecution is passed out. |
| return QueryExecutionFactory.create(query, dataset); |
| } |
| |
| @Override |
| public QueryExecutionBuilder newQuery() { |
| return QueryExecution.create().dataset(dataset); |
| } |
| |
| @Override |
| public void update(UpdateRequest update) { |
| checkOpen(); |
| Txn.executeWrite(dataset, ()->UpdateExecutionFactory.create(update, dataset).execute() ); |
| } |
| |
| @Override |
| public UpdateExecutionBuilder newUpdate() { |
| return UpdateExecution.create().dataset(dataset); |
| } |
| |
| @Override |
| public void load(String graph, String file) { |
| checkOpen(); |
| doPutPost(graph, file, false); |
| } |
| |
| @Override |
| public void load(String file) { |
| checkOpen(); |
| doPutPost(null, file, false); |
| } |
| |
| @Override |
| public void load(String graphName, Model model) { |
| checkOpen(); |
| Txn.executeWrite(dataset, ()-> { |
| Model modelDst = modelFor(graphName); |
| modelDst.add(model); |
| }); |
| } |
| |
| @Override |
| public void load(Model model) { |
| load(null, model); |
| } |
| |
| @Override |
| public Model fetch(String graph) { |
| return Txn.calculateRead(dataset, ()-> { |
| Model model = modelFor(graph); |
| return isolate(model); |
| }); |
| } |
| |
| @Override |
| public Model fetch() { |
| checkOpen(); |
| return fetch(null); |
| } |
| |
| @Override |
| public void put(String file) { |
| checkOpen(); |
| doPutPost(null, file, true); |
| } |
| |
| @Override |
| public void put(String graph, String file) { |
| checkOpen(); |
| doPutPost(graph, file, true); |
| } |
| |
| @Override |
| public void put(Model model) { |
| put(null, model); |
| } |
| |
| @Override |
| public void put(String graphName, Model model) { |
| checkOpen(); |
| Txn.executeWrite(dataset, ()-> { |
| Model modelDst = modelFor(graphName); |
| modelDst.removeAll(); |
| modelDst.add(model); |
| }); |
| } |
| |
| @Override |
| public void delete(String graph) { |
| checkOpen(); |
| Txn.executeWrite(dataset,() ->{ |
| if ( LibRDFConn.isDefault(graph) ) |
| dataset.getDefaultModel().removeAll(); |
| else |
| dataset.removeNamedModel(graph); |
| }); |
| } |
| |
| @Override |
| public void delete() { |
| checkOpen(); |
| delete(null); |
| } |
| |
| private void doPutPost(String graph, String file, boolean replace) { |
| Objects.requireNonNull(file); |
| Lang lang = RDFLanguages.filenameToLang(file); |
| |
| Txn.executeWrite(dataset,() ->{ |
| if ( RDFLanguages.isTriples(lang) ) { |
| Model model = LibRDFConn.isDefault(graph) ? dataset.getDefaultModel() : dataset.getNamedModel(graph); |
| if ( replace ) |
| model.removeAll(); |
| RDFDataMgr.read(model, file); |
| } |
| else if ( RDFLanguages.isQuads(lang) ) { |
| if ( replace ) |
| dataset.asDatasetGraph().clear(); |
| // Try to POST to the dataset. |
| RDFDataMgr.read(dataset, file); |
| } |
| else |
| throw new ARQException("Not an RDF format: "+file+" (lang="+lang+")"); |
| }); |
| } |
| |
| /** |
| * Called to isolate a model from its storage. |
| * Must be inside a transaction. |
| */ |
| private Model isolate(Model model) { |
| switch(isolation) { |
| case COPY: { |
| // Copy - the model is completely isolated from the original. |
| Model m2 = ModelFactory.createDefaultModel(); |
| m2.add(model); |
| return m2; |
| } |
| case READONLY : { |
| Graph g = new GraphReadOnly(model.getGraph()); |
| return ModelFactory.createModelForGraph(g); |
| } |
| case NONE : |
| return model; |
| } |
| throw new InternalErrorException(); |
| } |
| |
| /** |
| * Called to isolate a dataset from it's storage. |
| * Must be inside a transaction. |
| */ |
| private Dataset isolate(Dataset dataset) { |
| switch(isolation) { |
| case COPY: { |
| DatasetGraph dsg2 = DatasetGraphFactory.create(); |
| dataset.asDatasetGraph().find().forEachRemaining(q -> dsg2.add(q) ); |
| return DatasetFactory.wrap(dsg2); |
| } |
| case READONLY : { |
| DatasetGraph dsg = new DatasetGraphReadOnly(dataset.asDatasetGraph()); |
| return DatasetFactory.wrap(dsg); |
| } |
| case NONE : |
| return dataset; |
| } |
| throw new InternalErrorException(); |
| } |
| |
| private Model modelFor(String graph) { |
| if ( LibRDFConn.isDefault(graph) ) |
| return dataset.getDefaultModel(); |
| return dataset.getNamedModel(graph); |
| } |
| |
| @Override |
| public Dataset fetchDataset() { |
| checkOpen(); |
| return Txn.calculateRead(dataset,() -> isolate(dataset)); |
| } |
| |
| @Override |
| public void loadDataset(String file) { |
| checkOpen(); |
| Txn.executeWrite(dataset,() ->{ |
| RDFDataMgr.read(dataset, file); |
| }); |
| } |
| |
| @Override |
| public void loadDataset(Dataset srcDataset) { |
| checkOpen(); |
| srcDataset.executeRead(()->{ |
| dataset.executeWrite(()->{ |
| srcDataset.asDatasetGraph().find().forEachRemaining((q)->this.dataset.asDatasetGraph().add(q)); |
| }); |
| }); |
| } |
| |
| @Override |
| public void putDataset(String file) { |
| checkOpen(); |
| Txn.executeWrite(dataset,() ->{ |
| dataset.asDatasetGraph().clear(); |
| RDFDataMgr.read(dataset, file); |
| }); |
| } |
| |
| @Override |
| public void putDataset(Dataset dataset) { |
| Txn.executeWrite(dataset,() ->{ |
| this.dataset = isolate(dataset); |
| }); |
| } |
| |
| @Override |
| public void close() { |
| dataset = null; |
| } |
| |
| @Override |
| public boolean isClosed() { |
| return dataset == null; |
| } |
| |
| private void checkOpen() { |
| if ( dataset == null ) |
| throw new ARQException("closed"); |
| } |
| |
| @Override public void begin() { dataset.begin(); } |
| @Override public void begin(TxnType txnType) { dataset.begin(txnType); } |
| @Override public void begin(ReadWrite mode) { dataset.begin(mode); } |
| @Override public boolean promote(Promote promote) { return dataset.promote(promote); } |
| @Override public void commit() { dataset.commit(); } |
| @Override public void abort() { dataset.abort(); } |
| @Override public boolean isInTransaction() { return dataset.isInTransaction(); } |
| @Override public void end() { dataset.end(); } |
| @Override public ReadWrite transactionMode() { return dataset.transactionMode(); } |
| @Override public TxnType transactionType() { return dataset.transactionType(); } |
| } |
| |