1   package eu.fbk.knowledgestore.triplestore;
2   
3   import java.io.IOException;
4   
5   import javax.annotation.Nullable;
6   
7   import org.openrdf.model.Resource;
8   import org.openrdf.model.Statement;
9   import org.openrdf.model.URI;
10  import org.openrdf.model.Value;
11  import org.openrdf.query.BindingSet;
12  import org.openrdf.query.QueryEvaluationException;
13  
14  import info.aduna.iteration.CloseableIteration;
15  
16  import eu.fbk.knowledgestore.data.Handler;
17  import eu.fbk.knowledgestore.runtime.DataCorruptedException;
18  
19  /**
20   * A triple store transaction.
21   * <p>
22   * A {@code TripleTransaction} is a unit of work over the contents of a {@link TripleStore} that
23   * provides atomicity (i.e., changes are either completely stored or discarded), isolation (i.e.,
24   * other transaction do not see the modifications of this transaction) and durability (i.e.,
25   * changes are persisted across different program runs) guarantees.
26   * </p>
27   * <p>
28   * A {@code TripleTransaction} supports the following four main features:
29   * </p>
30   * <ul>
31   * <li><b>Statement retrieval</b>, via method {@link #get(Resource, URI, Value, Resource)};</li>
32   * <li><b>SPARQL querying</b>, via method {@link #query(SelectQuery, BindingSet)};</li>
33   * <li><b>Inference materialization</b>, via method {@link #infer(Handler)}</li>
34   * <li><b>Triple modification</b>, via bulk methods {@link #add(Iterable)} and
35   * {@link #remove(Iterable)}.</li>
36   * </ul>
37   * <p>
38   * Note that the latter two functionalities are not available for read-only transactions (an
39   * {@link IllegalStateException} is thrown in that case).
40   * </p>
41   * <p>
42   * Transactions are terminated via {@link #end(boolean)}, whose parameter specifies whether
43   * changes should be committed. Method {@code end()} always tries to terminate the transaction: if
44   * it throws an {@code IOException} exception a rollback must be assumed, even if a commit was
45   * asked; if it throws a {@code DataCorruptedException}, then neither commit or rollback were
46   * possible and the {@code TripleStore} is left in some unpredictable state with no possibility of
47   * automatic recovery. In that case, external code may resort to the {@link TripleStore#reset()}
48   * and re-population procedure to recover the situation. In case the JVM is abruptly shutdown
49   * during a transaction, the effects of the transaction should be the same as if a rollback was
50   * performed.
51   * </p>
52   * <p>
53   * {@code TripleTransaction} objects are not required to be thread safe. Access by at most one
54   * thread at a time can be assumed and must be guaranteed externally, with the only exception of
55   * method {@link #end(boolean)} that can be called at any moment by any thread in case the
56   * transaction need to be rolled back. Note also that it must be allowed for operations to be
57   * issued while streams from previous operations are still open; if a stream is open and a write
58   * operations is performed that affects one of the statements/query solutions still to be returned
59   * by the stream (or made a statement/query solution returnable/not returnable by the stream),
60   * then the behaviour of the stream is undefined.
61   * </p>
62   */
63  public interface TripleTransaction {
64  
65      /**
66       * Returns a Sesame {@code CloseableIteration} over all the RDF statements matching the
67       * (optional) subject, predicate, object, context supplied. Null values are used as wildcard.
68       * Implementations are designed to perform high throughput statement extraction (likely more
69       * efficient than doing a SPARQL query).
70       *
71       * @param subject
72       *            the subject to match, null to match any subject
73       * @param predicate
74       *            the predicate to match, null to match any predicate
75       * @param object
76       *            the object to match, null to match any object
77       * @param context
78       *            the context to match, null to match any context
79       * @return an iteration over matching RDF statements
80       * @throws IOException
81       *             in case some IO error occurs
82       * @throws IllegalStateException
83       *             if the {@code TripleTransaction} has been already ended
84       */
85      CloseableIteration<? extends Statement, ? extends Exception> get(@Nullable Resource subject,
86              @Nullable URI predicate, @Nullable Value object, @Nullable Resource context)
87              throws IOException, IllegalStateException;
88  
89      /**
90       * Evaluates the supplied SPARQL SELECT query, returning a Sesame {@code CloseableIteration}
91       * over its solutions. The method accepts an optional <tt>BindingSet</tt> object, providing
92       * bindings for variables in the query; it can be used to instantiate parametric queries,
93       * similarly to <tt>PreparedStatements</tt> in JDBC.
94       *
95       * @param query
96       *            the SPARQL SELECT query to evaluate
97       * @param bindings
98       *            optional bindings for variables in the query;
99       * @param timeout
100      *            optional timeout in milliseconds for the query
101      * @return an iteration over the results of the query
102      * @throws IOException
103      *             in case some IO error occurs
104      * @throws UnsupportedOperationException
105      *             in case the query, while being valid according to SPARQL, uses a construct,
106      *             function or feature that is not supported by the triple store implementation
107      *             (refer to the implementation documentation for unsupported features)
108      * @throws IllegalStateException
109      *             if the {@code TripleTransaction} has been already ended
110      */
111     CloseableIteration<BindingSet, QueryEvaluationException> query(SelectQuery query,
112             @Nullable BindingSet bindings, @Nullable Long timeout) throws IOException,
113             UnsupportedOperationException, IllegalStateException;
114 
115     /**
116      * Performs inference, materializing the logical closure of the RDF statements contained in
117      * the triple store. The method accepts an optional {@code Handler} inferred statements can be
118      * notified to, allowing for their additional processing by external code.
119      *
120      * @param handler
121      *            an optional handler inferred RDF statements can be notified to
122      * @throws IOException
123      *             in case some IO error occurs, with no guarantee that the {@code TripleStore} is
124      *             left in the same state if was when the method was called; note that this
125      *             exception may trigger a rollback on the caller side
126      * @throws IllegalStateException
127      *             if the {@code TripleTransaction} has been already ended, or if it is read-only
128      */
129     void infer(@Nullable Handler<? super Statement> handler) throws IOException,
130             IllegalStateException;
131 
132     /**
133      * Adds all the RDF statements in the {@code Iterable} specified to the triple store.
134      * Implementations are designed to perform high throughput insertion. They are also allowed to
135      * buffer part or all of the operation, successfully returning before specified triples have
136      * been completely modified; in this case, it must be however guaranteed that subsequent calls
137      * to {@link #query(SelectQuery, BindingSet)} will 'see' all the triples added.
138      *
139      * @param statements
140      *            the statements to add
141      * @throws IOException
142      *             in case some IO error occurs, with no guarantee that the {@code TripleStore} is
143      *             left in the same state if was when the method was called; note that this
144      *             exception may trigger a rollback on the caller side
145      * @throws IllegalStateException
146      *             if the {@code TripleTransaction} has been already ended, or if it is read-only
147      */
148     void add(Iterable<? extends Statement> statements) throws IOException, IllegalStateException;
149 
150     /**
151      * Removes all the RDF statements in the {@code Iterable} specified from the triple store.
152      * Implementations are designed to perform high throughput deletion. They are also allowed to
153      * return before specified triples have been completely removed; in this case, it must be
154      * however guaranteed that subsequent calls to {@link #query(SelectQuery, BindingSet)} will
155      * not 'see' any of the triples removed.
156      *
157      * @param statements
158      *            the statements to remove
159      * @throws IOException
160      *             in case some IO error occurs, with no guarantee that the {@code TripleStore} is
161      *             left in the same state if was when the method was called; note that this
162      *             exception may trigger a rollback on the caller side
163      * @throws IllegalStateException
164      *             if the {@code TripleTransaction} has been already ended, or if it is read-only
165      */
166     void remove(Iterable<? extends Statement> statements) throws IOException,
167             IllegalStateException;
168 
169     /**
170      * Ends the transaction, either committing or rolling back its changes (if any). This method
171      * always tries to terminate the transaction: if commit is requested but fails, a rollback is
172      * forced by the method and an {@code IOException} is thrown. If it is not possible either to
173      * commit or rollback, then the {@code TripleStore} is possibly left in an unknown state and a
174      * {@code DataCorruptedException} is thrown to signal a data corruption situation that cannot
175      * be automatically recovered (apart from calling {@link TripleStore#reset()} and repopulating
176      * the {@code TripleStore}).
177      *
178      * @param commit
179      *            true in case changes made by the transaction should be committed
180      * @throws IOException
181      *             in case some IO error occurs or the commit request cannot be satisfied for any
182      *             reason; it is however guaranteed that a forced rollback has been performed
183      * @throws DataCorruptedException
184      *             in case it was not possible either to commit or rollback, which implies the
185      *             state of the {@code TripleStore} is unknown and automatic recovery is not
186      *             possible (hence, data is corrupted)
187      * @throws IllegalStateException
188      *             if the {@code TripleTransaction} has been already ended
189      */
190     void end(boolean commit) throws DataCorruptedException, IOException, IllegalStateException;
191 
192 }
193 
194 // IMPLEMENTATION NOTES
195 //
196 // - ask, construct and describe queries can be implemented on top of select; a limit clause
197 // should be generated for ask queries for efficiency; a stream wrapper based on
198 // MultiProjectionIterator and the code of SailGraphQuery can be used for construct and describe.
199 // - during query execution, QueryEvaluationException is thrown in case of IO errors or if a
200 // malformed query is detected after execution began: in the first case an IOException seems more
201 // appropriate, while in the second case an UnsupportedOperationException seems appropriate
202 // (it's the client fault, not something expected to be recoverable)
203 //
204 // Corruption may result from
205 // - internal failure of the triple store (e.g. due to bug)
206 // - bug in the Java code or unsupported rollback that leaves partial modifications in the store