Skip to contentMethod: executeBooleanQuery(String)
1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.ontodriver.sesame.connector;
14:
15: import cz.cvut.kbss.ontodriver.config.DriverConfiguration;
16: import cz.cvut.kbss.ontodriver.exception.OntoDriverException;
17: import cz.cvut.kbss.ontodriver.sesame.config.SesameConfigParam;
18: import cz.cvut.kbss.ontodriver.sesame.exceptions.RepositoryCreationException;
19: import cz.cvut.kbss.ontodriver.sesame.exceptions.RepositoryNotFoundException;
20: import cz.cvut.kbss.ontodriver.sesame.exceptions.SesameDriverException;
21: import org.eclipse.rdf4j.common.iteration.Iterations;
22: import org.eclipse.rdf4j.model.*;
23: import org.eclipse.rdf4j.query.TupleQueryResult;
24: import org.eclipse.rdf4j.repository.Repository;
25: import org.eclipse.rdf4j.repository.RepositoryConnection;
26: import org.eclipse.rdf4j.repository.RepositoryException;
27: import org.eclipse.rdf4j.repository.RepositoryResult;
28: import org.eclipse.rdf4j.repository.config.RepositoryConfig;
29: import org.eclipse.rdf4j.repository.config.RepositoryConfigException;
30: import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
31: import org.eclipse.rdf4j.repository.manager.RepositoryManager;
32: import org.eclipse.rdf4j.repository.manager.RepositoryProvider;
33: import org.eclipse.rdf4j.repository.sail.SailRepository;
34: import org.eclipse.rdf4j.repository.sail.config.SailRepositoryConfig;
35: import org.eclipse.rdf4j.sail.Sail;
36: import org.eclipse.rdf4j.sail.config.SailImplConfig;
37: import org.eclipse.rdf4j.sail.helpers.SailWrapper;
38: import org.eclipse.rdf4j.sail.inferencer.fc.ForwardChainingRDFSInferencer;
39: import org.eclipse.rdf4j.sail.inferencer.fc.config.ForwardChainingRDFSInferencerConfig;
40: import org.eclipse.rdf4j.sail.memory.MemoryStore;
41: import org.eclipse.rdf4j.sail.nativerdf.config.NativeStoreConfig;
42: import org.slf4j.Logger;
43: import org.slf4j.LoggerFactory;
44:
45: import java.net.URI;
46: import java.util.Collection;
47: import java.util.List;
48: import java.util.Objects;
49:
50: class StorageConnector extends AbstractConnector {
51:
52: private static final String[] KNOWN_REMOTE_SCHEMES = {"http", "https", "ftp"};
53: private static final String LOCAL_NATIVE_REPO = "repositories/";
54: private static final String FILE_SCHEME = "file";
55:
56: private static final Logger LOG = LoggerFactory.getLogger(StorageConnector.class);
57:
58: private final DriverConfiguration configuration;
59:
60: private Repository repository;
61: private RepositoryManager manager;
62: private RepositoryConnection connection;
63:
64: StorageConnector(DriverConfiguration configuration) throws SesameDriverException {
65: assert configuration != null;
66:
67: this.configuration = configuration;
68: initialize();
69: this.open = true;
70: }
71:
72: private void initialize() throws SesameDriverException {
73: final URI serverUri = configuration.getStorageProperties().getPhysicalURI();
74: LOG.debug("Initializing connector to repository at {}", serverUri);
75: try {
76: final boolean isRemote = isRemoteRepository(serverUri);
77: if (isRemote) {
78: this.repository = connectToRemoteRepository(serverUri.toString());
79: } else {
80: this.repository = createLocalRepository();
81: }
82: verifyRepositoryCreated(serverUri, isRemote);
83: repository.initialize();
84: } catch (RepositoryException | RepositoryConfigException e) {
85: throw new SesameDriverException("Failed to acquire sesame repository connection.", e);
86: }
87: }
88:
89: private static boolean isRemoteRepository(URI uri) {
90: final String scheme = uri.getScheme();
91: for (String s : KNOWN_REMOTE_SCHEMES) {
92: if (s.equals(scheme)) {
93: return true;
94: }
95: }
96: return false;
97: }
98:
99: private Repository connectToRemoteRepository(String repoUri) {
100: this.manager = RepositoryProvider.getRepositoryManagerOfRepository(repoUri);
101: final RemoteRepositoryManager remoteManager = (RemoteRepositoryManager) manager;
102: final String username = configuration.getProperty(SesameConfigParam.USERNAME, "");
103: if (!username.isEmpty()) {
104: final String password = configuration.getProperty(SesameConfigParam.PASSWORD, "");
105: remoteManager.setUsernameAndPassword(username, password);
106: }
107: return manager.getRepository(RepositoryProvider.getRepositoryIdOfRepository(repoUri));
108: }
109:
110: private Repository createLocalRepository() {
111: final URI localUri = configuration.getStorageProperties().getPhysicalURI();
112: if (!isFileUri(localUri) && configuration.is(SesameConfigParam.USE_VOLATILE_STORAGE)) {
113: return createInMemoryRepository();
114: } else {
115: return createNativeRepository(configuration, localUri);
116: }
117: }
118:
119: private static boolean isFileUri(URI uri) {
120: return uri.getScheme() != null && uri.getScheme().equals(FILE_SCHEME);
121: }
122:
123: /**
124: * Creates a local in-memory Sesame repository which is disposed when the VM shuts down.
125: */
126: private Repository createInMemoryRepository() {
127: LOG.trace("Creating local in-memory repository.");
128: final MemoryStore ms = new MemoryStore();
129: if (configuration.is(SesameConfigParam.USE_INFERENCE)) {
130: return new SailRepository(new ForwardChainingRDFSInferencer(ms));
131: } else {
132: return new SailRepository(ms);
133: }
134: }
135:
136: /**
137: * Creates native repository.
138: * <p>
139: * This kind of repository stores data in files and is persistent after the VM shuts down.
140: */
141: private Repository createNativeRepository(DriverConfiguration configuration, final URI localUri) {
142: LOG.trace("Creating local native repository at " + localUri);
143: final String[] tmp = localUri.toString().split(LOCAL_NATIVE_REPO);
144: if (tmp.length != 2) {
145: throw new RepositoryCreationException(
146: "Unsupported local Sesame repository path. Expected file://path/repositories/id but got "
147: + localUri);
148: }
149: String repoId = tmp[1];
150: if (repoId.charAt(repoId.length() - 1) == '/') {
151: repoId = repoId.substring(0, repoId.length() - 1);
152: }
153: try {
154: this.manager = RepositoryProvider.getRepositoryManagerOfRepository(localUri.toASCIIString());
155: final RepositoryConfig cfg = createLocalNativeRepositoryConfig(repoId, configuration);
156: manager.addRepositoryConfig(cfg);
157: return manager.getRepository(repoId);
158: } catch (RepositoryConfigException | RepositoryException e) {
159: throw new RepositoryCreationException("Unable to create local repository at " + localUri, e);
160: }
161: }
162:
163: private static RepositoryConfig createLocalNativeRepositoryConfig(String repoId,
164: DriverConfiguration configuration) {
165: SailImplConfig backend = new NativeStoreConfig();
166: if (configuration.is(SesameConfigParam.USE_INFERENCE)) {
167: backend = new ForwardChainingRDFSInferencerConfig(backend);
168: }
169: final SailRepositoryConfig repoType = new SailRepositoryConfig(backend);
170: return new RepositoryConfig(repoId, repoType);
171: }
172:
173: private void verifyRepositoryCreated(URI serverUri, boolean isRemote) {
174: if (repository == null) {
175: if (isRemote) {
176: throw new RepositoryNotFoundException("Unable to reach repository at " + serverUri);
177: } else {
178: throw new RepositoryCreationException("Unable to create local repository at " + serverUri);
179: }
180: }
181: }
182:
183: @Override
184: public void close() throws SesameDriverException {
185: if (!open) {
186: return;
187: }
188: LOG.debug("Closing connector to repository {}.", configuration.getStorageProperties().getPhysicalURI());
189: try {
190: repository.shutDown();
191: if (manager != null) {
192: manager.shutDown();
193: }
194: } catch (RepositoryException e) {
195: throw new SesameDriverException("Exception caught when closing Sesame repository connection.", e);
196: } finally {
197: this.open = false;
198: }
199: }
200:
201: @Override
202: public TupleQueryResult executeSelectQuery(String query) throws SesameDriverException {
203: final RepositoryConnection connection = acquireConnection();
204: return new ConnectionStatementExecutor(connection).executeSelectQuery(query);
205: // The connection is released by the result set once it is closed
206: }
207:
208: RepositoryConnection acquireConnection() throws SesameDriverException {
209: // Workaround for local native storage being reset when multiple drivers access it
210: if (!repository.isInitialized()) {
211: initialize();
212: }
213: try {
214: LOG.trace("Acquiring repository connection.");
215: return repository.getConnection();
216: } catch (RepositoryException e) {
217: throw new SesameDriverException(e);
218: }
219: }
220:
221: void releaseConnection(RepositoryConnection connection) throws SesameDriverException {
222: try {
223: if (connection != null) {
224: LOG.trace("Releasing repository connection.");
225: connection.close();
226: }
227: } catch (RepositoryException e) {
228: throw new SesameDriverException(e);
229: }
230: }
231:
232: @Override
233: public boolean executeBooleanQuery(String query) throws SesameDriverException {
234: try (final RepositoryConnection connection = acquireConnection()) {
235: return new ConnectionStatementExecutor(connection).executeBooleanQuery(query);
236: }
237: }
238:
239: @Override
240: public void executeUpdate(String query) throws SesameDriverException {
241: try (final RepositoryConnection connection = acquireConnection()) {
242: new ConnectionStatementExecutor(connection).executeUpdate(query);
243: }
244: }
245:
246: @Override
247: public List<Resource> getContexts() throws SesameDriverException {
248: try (final RepositoryConnection connection = acquireConnection()) {
249: final RepositoryResult<Resource> res = connection.getContextIDs();
250: return Iterations.asList(res);
251: } catch (RepositoryException e) {
252: throw new SesameDriverException(e);
253: }
254: }
255:
256: @Override
257: public ValueFactory getValueFactory() {
258: return repository.getValueFactory();
259: }
260:
261: @Override
262: public void begin() throws SesameDriverException {
263: super.begin();
264: this.connection = acquireConnection();
265: try {
266: connection.begin();
267: } catch (RepositoryException e) {
268: transaction.rollback();
269: throw new SesameDriverException(e);
270: }
271: }
272:
273: @Override
274: public void commit() throws SesameDriverException {
275: assert connection != null;
276:
277: transaction.commit();
278: try {
279: connection.commit();
280: connection.close();
281: this.connection = null;
282: transaction.afterCommit();
283: } catch (RepositoryException e) {
284: transaction.rollback();
285: throw new SesameDriverException(e);
286: }
287: }
288:
289: @Override
290: public void rollback() throws SesameDriverException {
291: assert connection != null;
292: transaction.rollback();
293: try {
294: connection.rollback();
295: connection.close();
296: this.connection = null;
297: } catch (RepositoryException e) {
298: throw new SesameDriverException(e);
299: } finally {
300: transaction.afterRollback();
301: }
302: }
303:
304: @Override
305: public void addStatements(Collection<Statement> statements) throws SesameDriverException {
306: verifyTransactionActive();
307: assert connection != null;
308:
309: try {
310: connection.add(statements);
311: } catch (RepositoryException e) {
312: throw new SesameDriverException(e);
313: }
314: }
315:
316: @Override
317: public void removeStatements(Collection<Statement> statements) throws SesameDriverException {
318: verifyTransactionActive();
319: assert connection != null;
320:
321: try {
322: connection.remove(statements);
323: } catch (RepositoryException e) {
324: throw new SesameDriverException(e);
325: }
326: }
327:
328: @Override
329: public Collection<Statement> findStatements(Resource subject, IRI property, Value value, boolean includeInferred)
330: throws SesameDriverException {
331: return findStatements(subject, property, value, includeInferred, null);
332: }
333:
334: @Override
335: public Collection<Statement> findStatements(Resource subject, org.eclipse.rdf4j.model.IRI property,
336: Value value, boolean includeInferred, IRI context)
337: throws SesameDriverException {
338: try (final RepositoryConnection connection = acquireConnection()) {
339: final RepositoryResult<Statement> m;
340: if (context != null) {
341: m = connection.getStatements(subject, property, null, includeInferred, context);
342: } else {
343: m = connection.getStatements(subject, property, null, includeInferred);
344: }
345: return Iterations.asList(m);
346: } catch (RepositoryException e) {
347: throw new SesameDriverException(e);
348: }
349: }
350:
351: @Override
352: public boolean containsStatement(Resource subject, IRI property, Value value, boolean includeInferred)
353: throws SesameDriverException {
354: return containsStatement(subject, property, value, includeInferred, null);
355: }
356:
357: @Override
358: public boolean containsStatement(Resource subject, IRI property, Value value, boolean includeInferred, IRI context)
359: throws SesameDriverException {
360: try (final RepositoryConnection connection = acquireConnection()) {
361: if (context != null) {
362: return connection.hasStatement(subject, property, null, includeInferred, context);
363: } else {
364: return connection.hasStatement(subject, property, null, includeInferred);
365: }
366: } catch (RepositoryException e) {
367: throw new SesameDriverException(e);
368: }
369: }
370:
371: @Override
372: public <T> T unwrap(Class<T> cls) throws OntoDriverException {
373: if (cls.isAssignableFrom(getClass())) {
374: return cls.cast(this);
375: }
376: if (cls.isAssignableFrom(repository.getClass())) {
377: return cls.cast(repository);
378: }
379: throw new SesameDriverException("No instance of class " + cls + " found.");
380: }
381:
382: /**
383: * Replaces the currently open repository with the specified one.
384: * <p>
385: * Note that this functionality is only supported for in-memory stores.
386: *
387: * @param newRepository The new repository to set
388: */
389: public void setRepository(Repository newRepository) {
390: Objects.requireNonNull(newRepository);
391: if (!isInMemoryRepository(repository)) {
392: throw new UnsupportedOperationException("Cannot replace repository which is not in-memory.");
393: }
394: if (transaction.isActive()) {
395: throw new IllegalStateException("Cannot replace repository in transaction.");
396: }
397: repository.shutDown();
398: assert newRepository.isInitialized();
399: this.repository = newRepository;
400: // Since in-memory repositories are not registered in RepositoryManager, we shouldn't need to deal with it
401: }
402:
403: private static boolean isInMemoryRepository(Repository repo) {
404: if (!(repo instanceof SailRepository)) {
405: return false;
406: }
407: final Sail sail = ((SailRepository) repo).getSail();
408: return sail instanceof SailWrapper ? ((SailWrapper) sail).getBaseSail() instanceof MemoryStore :
409: sail instanceof MemoryStore;
410: }
411: }