001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.nativerdf;
007:
008: import java.io.File;
009: import java.io.IOException;
010: import java.util.ArrayList;
011: import java.util.Collections;
012: import java.util.LinkedHashSet;
013: import java.util.List;
014:
015: import info.aduna.concurrent.locks.ExclusiveLockManager;
016: import info.aduna.concurrent.locks.Lock;
017: import info.aduna.concurrent.locks.ReadWriteLockManager;
018: import info.aduna.concurrent.locks.WritePrefReadWriteLockManager;
019: import info.aduna.iteration.CloseableIteration;
020: import info.aduna.iteration.EmptyIteration;
021: import info.aduna.iteration.UnionIteration;
022:
023: import org.openrdf.model.Resource;
024: import org.openrdf.model.Statement;
025: import org.openrdf.model.URI;
026: import org.openrdf.model.Value;
027: import org.openrdf.model.ValueFactory;
028: import org.openrdf.sail.SailConnection;
029: import org.openrdf.sail.SailException;
030: import org.openrdf.sail.helpers.SailBase;
031: import org.openrdf.sail.nativerdf.btree.RecordIterator;
032: import org.openrdf.sail.nativerdf.model.NativeValue;
033:
034: /**
035: * A SAIL implementation using B-Tree indexing on disk for storing and querying
036: * its data.
037: *
038: * @author Arjohn Kampman
039: * @author jeen
040: */
041: public class NativeStore extends SailBase {
042:
043: /*-----------*
044: * Variables *
045: *-----------*/
046:
047: /**
048: * Specifies which triple indexes this native store must use.
049: */
050: private String tripleIndexes;
051:
052: /**
053: * Flag indicating whether updates should be synced to disk forcefully. This
054: * may have a severe impact on write performance. By default, this feature is
055: * disabled.
056: */
057: private boolean forceSync = false;
058:
059: private TripleStore tripleStore;
060:
061: private ValueStore valueStore;
062:
063: private NamespaceStore namespaceStore;
064:
065: /**
066: * Lock manager used to synchronize read and write access to the store.
067: */
068: private ReadWriteLockManager storeLockManager;
069:
070: /**
071: * Lock manager used to prevent concurrent transactions.
072: */
073: private ExclusiveLockManager txnLockManager;
074:
075: private boolean trackLocks = false;
076:
077: /**
078: * Flag indicating whether the Sail has been initialized.
079: */
080: private boolean initialized;
081:
082: /*--------------*
083: * Constructors *
084: *--------------*/
085:
086: /**
087: * Creates a new NativeStore.
088: */
089: public NativeStore() {
090: initialized = false;
091: }
092:
093: public NativeStore(File dataDir) {
094: this ();
095: setDataDir(dataDir);
096: }
097:
098: public NativeStore(File dataDir, String tripleIndexes) {
099: this (dataDir);
100: setTripleIndexes(tripleIndexes);
101: }
102:
103: /*---------*
104: * Methods *
105: *---------*/
106:
107: /**
108: * Sets the triple indexes for the native store, must be called before
109: * initialization.
110: *
111: * @param tripleIndexes
112: * An index strings, e.g. <tt>spoc,posc</tt>.
113: */
114: public void setTripleIndexes(String tripleIndexes) {
115: if (isInitialized()) {
116: throw new IllegalStateException(
117: "sail has already been intialized");
118: }
119:
120: this .tripleIndexes = tripleIndexes;
121: }
122:
123: public String getTripleIndexes() {
124: return tripleIndexes;
125: }
126:
127: /**
128: * Specifiec whether updates should be synced to disk forcefully, must be
129: * called before initialization. Enabling this feature may prevent corruption
130: * in case of events like power loss, but can have a severe impact on write
131: * performance. By default, this feature is disabled.
132: */
133: public void setForceSync(boolean forceSync) {
134: this .forceSync = forceSync;
135: }
136:
137: public boolean getForceSync() {
138: return forceSync;
139: }
140:
141: /**
142: * Initializes this NativeStore.
143: *
144: * @exception SailException
145: * If this RdfRepository could not be initialized using the
146: * parameters that have been set.
147: */
148: public void initialize() throws SailException {
149: if (isInitialized()) {
150: throw new IllegalStateException(
151: "sail has already been intialized");
152: }
153:
154: logger.debug("Initializing NativeStore...");
155:
156: storeLockManager = new WritePrefReadWriteLockManager(trackLocks);
157: txnLockManager = new ExclusiveLockManager(trackLocks);
158:
159: // Check initialization parameters
160: File dataDir = getDataDir();
161:
162: if (dataDir == null) {
163: throw new SailException("Data dir has not been set");
164: } else if (!dataDir.exists()) {
165: boolean success = dataDir.mkdirs();
166: if (!success) {
167: throw new SailException(
168: "Unable to create data directory: " + dataDir);
169: }
170: } else if (!dataDir.isDirectory()) {
171: throw new SailException(
172: "The specified path does not denote a directory: "
173: + dataDir);
174: } else if (!dataDir.canRead()) {
175: throw new SailException(
176: "Not allowed to read from the specified directory: "
177: + dataDir);
178: }
179:
180: logger.debug("Data dir is " + dataDir);
181:
182: try {
183: namespaceStore = new NamespaceStore(dataDir);
184: valueStore = new ValueStore(dataDir, forceSync);
185: tripleStore = new TripleStore(dataDir, tripleIndexes,
186: forceSync);
187: } catch (IOException e) {
188: throw new SailException(e);
189: }
190:
191: initialized = true;
192: logger.debug("NativeStore initialized");
193: }
194:
195: /**
196: * Checks whether the Sail has been initialized.
197: *
198: * @return <tt>true</tt> if the Sail has been initialized, <tt>false</tt>
199: * otherwise.
200: */
201: protected final boolean isInitialized() {
202: return initialized;
203: }
204:
205: @Override
206: protected void shutDownInternal() throws SailException {
207: if (isInitialized()) {
208: logger.debug("Shutting down NativeStore...");
209:
210: Lock txnLock = getTransactionLock();
211: try {
212: Lock writeLock = getWriteLock();
213: try {
214: tripleStore.close();
215: valueStore.close();
216: namespaceStore.close();
217:
218: initialized = false;
219:
220: logger.debug("NativeStore shut down");
221: } catch (IOException e) {
222: throw new SailException(e);
223: } finally {
224: writeLock.release();
225: }
226: } finally {
227: txnLock.release();
228: }
229: }
230: }
231:
232: public boolean isWritable() {
233: return getDataDir().canWrite();
234: }
235:
236: @Override
237: protected SailConnection getConnectionInternal()
238: throws SailException {
239: if (!isInitialized()) {
240: throw new IllegalStateException("sail not initialized.");
241: }
242:
243: try {
244: return new NativeStoreConnection(this );
245: } catch (IOException e) {
246: throw new SailException(e);
247: }
248: }
249:
250: public ValueFactory getValueFactory() {
251: return valueStore;
252: }
253:
254: protected TripleStore getTripleStore() {
255: return tripleStore;
256: }
257:
258: protected ValueStore getValueStore() {
259: return valueStore;
260: }
261:
262: protected NamespaceStore getNamespaceStore() {
263: return namespaceStore;
264: }
265:
266: protected Lock getReadLock() throws SailException {
267: try {
268: return storeLockManager.getReadLock();
269: } catch (InterruptedException e) {
270: throw new SailException(e);
271: }
272: }
273:
274: protected Lock getWriteLock() throws SailException {
275: try {
276: return storeLockManager.getWriteLock();
277: } catch (InterruptedException e) {
278: throw new SailException(e);
279: }
280: }
281:
282: protected Lock getTransactionLock() throws SailException {
283: try {
284: return txnLockManager.getExclusiveLock();
285: } catch (InterruptedException e) {
286: throw new SailException(e);
287: }
288: }
289:
290: protected List<Integer> getContextIDs(Resource... contexts)
291: throws IOException {
292: assert contexts.length > 0 : "contexts must not be empty";
293:
294: // Filter duplicates
295: LinkedHashSet<Resource> contextSet = new LinkedHashSet<Resource>();
296: Collections.addAll(contextSet, contexts);
297:
298: // Fetch IDs, filtering unknown resources from the result
299: List<Integer> contextIDs = new ArrayList<Integer>(contextSet
300: .size());
301: for (Resource context : contextSet) {
302: if (context == null) {
303: contextIDs.add(0);
304: } else {
305: int contextID = valueStore.getID(context);
306: if (contextID != NativeValue.UNKNOWN_ID) {
307: contextIDs.add(contextID);
308: }
309: }
310: }
311:
312: return contextIDs;
313: }
314:
315: /**
316: * Creates a statement iterator based on the supplied pattern.
317: *
318: * @param subj
319: * The subject of the pattern, or <tt>null</tt> to indicate a
320: * wildcard.
321: * @param pred
322: * The predicate of the pattern, or <tt>null</tt> to indicate a
323: * wildcard.
324: * @param obj
325: * The object of the pattern, or <tt>null</tt> to indicate a
326: * wildcard.
327: * @param context
328: * The context of the pattern, or <tt>null</tt> to indicate a
329: * wildcard
330: * @return A StatementIterator that can be used to iterate over the
331: * statements that match the specified pattern.
332: */
333: protected CloseableIteration<? extends Statement, IOException> createStatementIterator(
334: Resource subj, URI pred, Value obj,
335: boolean includeInferred, boolean readTransaction,
336: Resource... contexts) throws IOException {
337: int subjID = NativeValue.UNKNOWN_ID;
338: if (subj != null) {
339: subjID = valueStore.getID(subj);
340: if (subjID == NativeValue.UNKNOWN_ID) {
341: return new EmptyIteration<Statement, IOException>();
342: }
343: }
344:
345: int predID = NativeValue.UNKNOWN_ID;
346: if (pred != null) {
347: predID = valueStore.getID(pred);
348: if (predID == NativeValue.UNKNOWN_ID) {
349: return new EmptyIteration<Statement, IOException>();
350: }
351: }
352:
353: int objID = NativeValue.UNKNOWN_ID;
354: if (obj != null) {
355: objID = valueStore.getID(obj);
356: if (objID == NativeValue.UNKNOWN_ID) {
357: return new EmptyIteration<Statement, IOException>();
358: }
359: }
360:
361: List<Integer> contextIDList = new ArrayList<Integer>(
362: contexts.length);
363: if (contexts.length == 0) {
364: contextIDList.add(NativeValue.UNKNOWN_ID);
365: } else {
366: for (Resource context : contexts) {
367: if (context == null) {
368: contextIDList.add(0);
369: } else {
370: int contextID = valueStore.getID(context);
371:
372: if (contextID != NativeValue.UNKNOWN_ID) {
373: contextIDList.add(contextID);
374: }
375: }
376: }
377: }
378:
379: ArrayList<NativeStatementIterator> perContextIterList = new ArrayList<NativeStatementIterator>(
380: contextIDList.size());
381:
382: for (int contextID : contextIDList) {
383: RecordIterator btreeIter;
384:
385: if (includeInferred) {
386: // Get both explicit and inferred statements
387: btreeIter = tripleStore.getTriples(subjID, predID,
388: objID, contextID, readTransaction);
389: } else {
390: // Only get explicit statements
391: btreeIter = tripleStore.getTriples(subjID, predID,
392: objID, contextID, true, readTransaction);
393: }
394:
395: perContextIterList.add(new NativeStatementIterator(
396: btreeIter, valueStore));
397: }
398:
399: return new UnionIteration<Statement, IOException>(
400: perContextIterList);
401: }
402: }
|