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;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.ByteArrayOutputStream;
010: import java.io.ObjectInputStream;
011: import java.io.ObjectOutputStream;
012:
013: import junit.framework.TestCase;
014:
015: import info.aduna.iteration.CloseableIteration;
016: import info.aduna.iteration.Iteration;
017: import info.aduna.iteration.Iterations;
018:
019: import org.openrdf.model.BNode;
020: import org.openrdf.model.Literal;
021: import org.openrdf.model.Namespace;
022: import org.openrdf.model.Resource;
023: import org.openrdf.model.Statement;
024: import org.openrdf.model.URI;
025: import org.openrdf.model.Value;
026: import org.openrdf.model.ValueFactory;
027: import org.openrdf.model.impl.BNodeImpl;
028: import org.openrdf.model.impl.LiteralImpl;
029: import org.openrdf.model.impl.NumericLiteralImpl;
030: import org.openrdf.model.impl.URIImpl;
031: import org.openrdf.model.vocabulary.RDF;
032: import org.openrdf.model.vocabulary.RDFS;
033: import org.openrdf.query.BindingSet;
034: import org.openrdf.query.QueryEvaluationException;
035: import org.openrdf.query.QueryLanguage;
036: import org.openrdf.query.algebra.TupleExpr;
037: import org.openrdf.query.impl.EmptyBindingSet;
038: import org.openrdf.query.impl.MapBindingSet;
039: import org.openrdf.query.parser.ParsedTupleQuery;
040: import org.openrdf.query.parser.QueryParserUtil;
041:
042: /**
043: * A JUnit test for testing Sail implementations that store RDF data. This is
044: * purely a test for data storage and retrieval which assumes that no
045: * inferencing or whatsoever is performed. This is an abstract class that should
046: * be extended for specific Sail implementations.
047: */
048: public abstract class RDFStoreTest extends TestCase implements
049: SailChangedListener {
050:
051: /*-----------*
052: * Constants *
053: *-----------*/
054:
055: private static final String EXAMPLE_NS = "http://example.org/";
056:
057: private static final String PAINTER = "Painter";
058:
059: private static final String PAINTS = "paints";
060:
061: private static final String PAINTING = "Painting";
062:
063: private static final String PICASSO = "picasso";
064:
065: private static final String REMBRANDT = "rembrandt";
066:
067: private static final String GUERNICA = "guernica";
068:
069: private static final String NIGHTWATCH = "nightwatch";
070:
071: private static final String CONTEXT_1 = "context1";
072:
073: private static final String CONTEXT_2 = "context2";
074:
075: /*-----------*
076: * Variables *
077: *-----------*/
078:
079: private URI painter;
080:
081: private URI paints;
082:
083: private URI painting;
084:
085: private URI picasso;
086:
087: private URI rembrandt;
088:
089: private URI guernica;
090:
091: private URI nightwatch;
092:
093: private URI context1;
094:
095: private URI context2;
096:
097: private Sail sail;
098:
099: private SailConnection con;
100:
101: private ValueFactory vf;
102:
103: private int removeEventCount;
104:
105: private int addEventCount;
106:
107: /*--------------*
108: * Constructors *
109: *--------------*/
110:
111: public RDFStoreTest(String name) {
112: super (name);
113: }
114:
115: /*---------*
116: * Methods *
117: *---------*/
118:
119: /**
120: * Gets an instance of the Sail that should be tested. The returned
121: * repository should already have been initialized.
122: *
123: * @return an initialized Sail.
124: * @throws SailException
125: * If the initialization of the repository failed.
126: */
127: protected abstract Sail createSail() throws SailException;
128:
129: @Override
130: protected void setUp() throws Exception {
131: sail = createSail();
132:
133: // set self as listener
134: sail.addSailChangedListener(this );
135:
136: con = sail.getConnection();
137:
138: // Create values
139: vf = sail.getValueFactory();
140:
141: painter = vf.createURI(EXAMPLE_NS, PAINTER);
142: paints = vf.createURI(EXAMPLE_NS, PAINTS);
143: painting = vf.createURI(EXAMPLE_NS, PAINTING);
144: picasso = vf.createURI(EXAMPLE_NS, PICASSO);
145: guernica = vf.createURI(EXAMPLE_NS, GUERNICA);
146: rembrandt = vf.createURI(EXAMPLE_NS, REMBRANDT);
147: nightwatch = vf.createURI(EXAMPLE_NS, NIGHTWATCH);
148:
149: context1 = vf.createURI(EXAMPLE_NS, CONTEXT_1);
150: context2 = vf.createURI(EXAMPLE_NS, CONTEXT_2);
151:
152: }
153:
154: @Override
155: protected void tearDown() throws Exception {
156: try {
157: con.close();
158: } finally {
159: sail.shutDown();
160: sail = null;
161: }
162: }
163:
164: public void testEmptyRepository() throws Exception {
165: // repository should be empty
166: assertEquals(
167: "Empty repository should not return any statements", 0,
168: countAllElements());
169:
170: assertEquals("Named context should be empty", 0,
171: countContext1Elements());
172:
173: assertEquals(
174: "Empty repository should not return any context identifiers",
175: 0, countElements(con.getContextIDs()));
176:
177: assertEquals(
178: "Empty repository should not return any query results",
179: 0, countQueryResults("select * from {S} P {O}"));
180: }
181:
182: public void testValueRoundTrip1() throws Exception {
183: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
184: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
185: URI obj = new URIImpl(EXAMPLE_NS + GUERNICA);
186:
187: testValueRoundTrip(subj, pred, obj);
188: }
189:
190: public void testValueRoundTrip2() throws Exception {
191: BNode subj = new BNodeImpl("foo");
192: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
193: URI obj = new URIImpl(EXAMPLE_NS + GUERNICA);
194:
195: testValueRoundTrip(subj, pred, obj);
196: }
197:
198: public void testValueRoundTrip3() throws Exception {
199: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
200: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
201: Literal obj = new LiteralImpl("guernica");
202:
203: testValueRoundTrip(subj, pred, obj);
204: }
205:
206: public void testValueRoundTrip4() throws Exception {
207: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
208: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
209: Literal obj = new LiteralImpl("guernica", "es");
210:
211: testValueRoundTrip(subj, pred, obj);
212: }
213:
214: public void testValueRoundTrip5() throws Exception {
215: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
216: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
217: Literal obj = new NumericLiteralImpl(3);
218:
219: testValueRoundTrip(subj, pred, obj);
220: }
221:
222: public void testLongURIRoundTrip() throws Exception {
223: StringBuffer sb = new StringBuffer();
224: for (int i = 0; i < 512; i++) {
225: sb.append(Character.toChars('A' + (i % 26)));
226: }
227: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
228: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
229: URI obj = new URIImpl(EXAMPLE_NS + GUERNICA + sb.toString());
230:
231: testValueRoundTrip(subj, pred, obj);
232: }
233:
234: public void testLongLiteralRoundTrip() throws Exception {
235: StringBuffer sb = new StringBuffer();
236: for (int i = 0; i < 512; i++) {
237: sb.append(Character.toChars('A' + (i % 26)));
238: }
239: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
240: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
241: Literal obj = new LiteralImpl("guernica" + sb.toString());
242:
243: testValueRoundTrip(subj, pred, obj);
244: }
245:
246: public void testLongLangRoundTrip() throws Exception {
247: StringBuffer sb = new StringBuffer();
248: for (int i = 0; i < 512; i++) {
249: sb.append(Character.toChars('A' + (i % 26)));
250: }
251: URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
252: URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
253: Literal obj = new LiteralImpl("guernica" + sb.toString(), "es");
254:
255: testValueRoundTrip(subj, pred, obj);
256: }
257:
258: private void testValueRoundTrip(Resource subj, URI pred, Value obj)
259: throws Exception {
260: con.addStatement(subj, pred, obj);
261: con.commit();
262:
263: CloseableIteration<? extends Statement, SailException> stIter = con
264: .getStatements(null, null, null, false);
265:
266: try {
267: assertTrue(stIter.hasNext());
268:
269: Statement st = stIter.next();
270: assertEquals(subj, st.getSubject());
271: assertEquals(pred, st.getPredicate());
272: assertEquals(obj, st.getObject());
273: assertTrue(!stIter.hasNext());
274: } finally {
275: stIter.close();
276: }
277:
278: ParsedTupleQuery tupleQuery = QueryParserUtil.parseTupleQuery(
279: QueryLanguage.SERQL,
280: "SELECT S, P, O FROM {S} P {O} WHERE P = <"
281: + pred.stringValue() + ">", null);
282:
283: CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
284: iter = con.evaluate(tupleQuery.getTupleExpr(), null,
285: EmptyBindingSet.getInstance(), false);
286:
287: try {
288: assertTrue(iter.hasNext());
289:
290: BindingSet bindings = iter.next();
291: assertEquals(subj, bindings.getValue("S"));
292: assertEquals(pred, bindings.getValue("P"));
293: assertEquals(obj, bindings.getValue("O"));
294: assertTrue(!iter.hasNext());
295: } finally {
296: iter.close();
297: }
298: }
299:
300: public void testCreateURI1() throws Exception {
301: URI picasso1 = vf.createURI(EXAMPLE_NS, PICASSO);
302: URI picasso2 = vf.createURI(EXAMPLE_NS + PICASSO);
303: con.addStatement(picasso1, paints, guernica);
304: con.addStatement(picasso2, paints, guernica);
305: con.commit();
306:
307: assertEquals(
308: "createURI(Sring) and createURI(String, String) should create equal URIs",
309: 1, con.size());
310: }
311:
312: public void testCreateURI2() throws Exception {
313: URI picasso1 = vf.createURI(EXAMPLE_NS + PICASSO);
314: URI picasso2 = vf.createURI(EXAMPLE_NS, PICASSO);
315: con.addStatement(picasso1, paints, guernica);
316: con.addStatement(picasso2, paints, guernica);
317: con.commit();
318:
319: assertEquals(
320: "createURI(Sring) and createURI(String, String) should create equal URIs",
321: 1, con.size());
322: }
323:
324: public void testSize() throws Exception {
325: assertEquals("Size of empty repository should be 0", 0, con
326: .size());
327:
328: // Add some data to the repository
329: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
330: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
331: con.addStatement(picasso, RDF.TYPE, painter, context1);
332: con.addStatement(guernica, RDF.TYPE, painting, context1);
333: con.addStatement(picasso, paints, guernica, context1);
334: con.commit();
335:
336: assertEquals("Size of repository should be 5", 5, con.size());
337: assertEquals("Size of named context should be 3", 3, con
338: .size(context1));
339:
340: URI unknownContext = new URIImpl(EXAMPLE_NS + "unknown");
341:
342: assertEquals("Size of unknown context should be 0", 0, con
343: .size(unknownContext));
344:
345: URIImpl uriImplContext1 = new URIImpl(context1.toString());
346:
347: assertEquals(
348: "Size of named context (defined as URIImpl) should be 3",
349: 3, con.size(uriImplContext1));
350: }
351:
352: public void testAddData() throws Exception {
353: // Add some data to the repository
354: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
355: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
356: con.addStatement(picasso, RDF.TYPE, painter, context1);
357: con.addStatement(guernica, RDF.TYPE, painting, context1);
358: con.addStatement(picasso, paints, guernica, context1);
359: con.commit();
360:
361: assertEquals("Repository should contain 5 statements in total",
362: 5, countAllElements());
363:
364: assertEquals("Named context should contain 3 statements", 3,
365: countContext1Elements());
366:
367: assertEquals("Repository should have 1 context identifier", 1,
368: countElements(con.getContextIDs()));
369:
370: assertEquals("Repository should contain 5 statements in total",
371: 5, countQueryResults("select * from {S} P {O}"));
372:
373: // Check for presence of the added statements
374: assertEquals(
375: "Statement (Painter, type, Class) should be in the repository",
376: 1,
377: countQueryResults("select 1 from {ex:Painter} rdf:type {rdfs:Class}"));
378:
379: assertEquals(
380: "Statement (picasso, type, Painter) should be in the repository",
381: 1,
382: countQueryResults("select 1 from {ex:picasso} rdf:type {ex:Painter}"));
383:
384: // Check for absense of non-added statements
385: assertEquals(
386: "Statement (Painter, paints, Painting) should not be in the repository",
387: 0,
388: countQueryResults("select 1 from {ex:Painter} ex:paints {ex:Painting}"));
389:
390: assertEquals(
391: "Statement (picasso, creates, guernica) should not be in the repository",
392: 0,
393: countQueryResults("select 1 from {ex:picasso} ex:creates {ex:guernica}"));
394:
395: // Various other checks
396: assertEquals(
397: "Repository should contain 2 statements matching (picasso, _, _)",
398: 2,
399: countQueryResults("select * from {ex:picasso} P {O}"));
400:
401: assertEquals(
402: "Repository should contain 1 statement matching (picasso, paints, _)",
403: 1,
404: countQueryResults("select * from {ex:picasso} ex:paints {O}"));
405:
406: assertEquals(
407: "Repository should contain 4 statements matching (_, type, _)",
408: 4, countQueryResults("select * from {S} rdf:type {O}"));
409:
410: assertEquals(
411: "Repository should contain 2 statements matching (_, _, Class)",
412: 2,
413: countQueryResults("select * from {S} P {rdfs:Class}"));
414:
415: assertEquals(
416: "Repository should contain 0 statements matching (_, _, type)",
417: 0, countQueryResults("select * from {S} P {rdf:type}"));
418: }
419:
420: public void testAddWhileQuerying() throws Exception {
421: // Add some data to the repository
422: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
423: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
424: con.addStatement(picasso, RDF.TYPE, painter);
425: con.addStatement(guernica, RDF.TYPE, painting);
426: con.addStatement(picasso, paints, guernica);
427: con.commit();
428:
429: ParsedTupleQuery tupleQuery = QueryParserUtil.parseTupleQuery(
430: QueryLanguage.SERQL, "SELECT C FROM {} rdf:type {C}",
431: null);
432:
433: CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
434: iter = con.evaluate(tupleQuery.getTupleExpr(), null,
435: EmptyBindingSet.getInstance(), false);
436:
437: while (iter.hasNext()) {
438: BindingSet bindings = iter.next();
439: Value c = bindings.getValue("C");
440: if (c instanceof Resource) {
441: con.addStatement((Resource) c, RDF.TYPE, RDFS.CLASS);
442: }
443: }
444:
445: con.commit();
446:
447: // Simulate auto-commit
448:
449: assertEquals(3, countElements(con.getStatements(null, RDF.TYPE,
450: RDFS.CLASS, false)));
451:
452: tupleQuery = QueryParserUtil.parseTupleQuery(
453: QueryLanguage.SERQL, "SELECT P FROM {} P {}", null);
454: iter = con.evaluate(tupleQuery.getTupleExpr(), null,
455: EmptyBindingSet.getInstance(), false);
456:
457: while (iter.hasNext()) {
458: BindingSet bindings = iter.next();
459: Value p = bindings.getValue("P");
460: if (p instanceof URI) {
461: con.addStatement((URI) p, RDF.TYPE, RDF.PROPERTY);
462: con.commit();
463: }
464: }
465:
466: assertEquals(2, countElements(con.getStatements(null, RDF.TYPE,
467: RDF.PROPERTY, false)));
468: }
469:
470: public void testRemoveAndClear() throws Exception {
471: // Add some data to the repository
472: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
473: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
474: con.addStatement(picasso, RDF.TYPE, painter, context1);
475: con.addStatement(guernica, RDF.TYPE, painting, context1);
476: con.addStatement(picasso, paints, guernica, context1);
477: con.commit();
478:
479: // Test removal of statements
480: con.removeStatements(painting, RDF.TYPE, RDFS.CLASS);
481: con.commit();
482:
483: assertEquals("Repository should contain 4 statements in total",
484: 4, countAllElements());
485:
486: assertEquals("Named context should contain 3 statements", 3,
487: countContext1Elements());
488:
489: assertEquals(
490: "Statement (Painting, type, Class) should no longer be in the repository",
491: 0,
492: countQueryResults("select 1 from {ex:Painting} rdf:type {rdfs:Class}"));
493:
494: con.removeStatements(null, null, null, context1);
495: con.commit();
496:
497: assertEquals("Repository should contain 1 statement in total",
498: 1, countAllElements());
499:
500: assertEquals("Named context should be empty", 0,
501: countContext1Elements());
502:
503: con.clear();
504: con.commit();
505:
506: assertEquals(
507: "Repository should no longer contain any statements",
508: 0, countAllElements());
509:
510: // test if event listener works properly.
511: assertEquals(
512: "There should have been 1 event in which statements were added",
513: 1, addEventCount);
514:
515: assertEquals(
516: "There should have been 3 events in which statements were removed",
517: 3, removeEventCount);
518: }
519:
520: public void testClose() {
521: try {
522: con.close();
523: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
524: fail("Operation on connection after close should result in IllegalStateException");
525: } catch (IllegalStateException e) {
526: // do nothing, this is expected
527: } catch (SailException e) {
528: fail(e.getMessage());
529: }
530: }
531:
532: public void testContexts() throws Exception {
533: // Add schema data to the repository, no context
534: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
535: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
536:
537: // Add stuff about picasso to context1
538: con.addStatement(picasso, RDF.TYPE, painter, context1);
539: con.addStatement(guernica, RDF.TYPE, painting, context1);
540: con.addStatement(picasso, paints, guernica, context1);
541:
542: // Add stuff about rembrandt to context2
543: con.addStatement(rembrandt, RDF.TYPE, painter, context2);
544: con.addStatement(nightwatch, RDF.TYPE, painting, context2);
545: con.addStatement(rembrandt, paints, nightwatch, context2);
546:
547: con.commit();
548:
549: assertEquals("context1 should contain 3 statements", 3,
550: countContext1Elements());
551: assertEquals("context2 should contain 3 statements", 3,
552: countElements(con.getStatements(null, null, null,
553: false, context2)));
554: assertEquals("Repository should contain 8 statements", 8,
555: countAllElements());
556: assertEquals("statements without context should equal 2", 2,
557: countElements(con.getStatements(null, null, null,
558: false, (Resource) null)));
559:
560: assertEquals(
561: "Statements without context and statements in context 1 together should total 5",
562: 5, countElements(con.getStatements(null, null, null,
563: false, null, context1)));
564:
565: assertEquals(
566: "Statements without context and statements in context 2 together should total 5",
567: 5, countElements(con.getStatements(null, null, null,
568: false, null, context2)));
569:
570: assertEquals(
571: "Statements in context 1 and in context 2 together should total 6",
572: 6, countElements(con.getStatements(null, null, null,
573: false, context1, context2)));
574:
575: // remove two statements from context1.
576: con.removeStatements(picasso, null, null, context1);
577: con.commit();
578:
579: assertEquals("context1 should contain 1 statements", 1,
580: countContext1Elements());
581:
582: assertEquals("Repository should contain 6 statements", 6,
583: countAllElements());
584:
585: assertEquals(
586: "Statements without context and statements in context 1 together should total 3",
587: 3, countElements(con.getStatements(null, null, null,
588: false, null, context1)));
589:
590: assertEquals(
591: "Statements without context and statements in context 2 together should total 5",
592: 5, countElements(con.getStatements(null, null, null,
593: false, context2, null)));
594:
595: assertEquals(
596: "Statements in context 1 and in context 2 together should total 4",
597: 4, countElements(con.getStatements(null, null, null,
598: false, context1, context2)));
599: }
600:
601: public void testQueryBindings() throws Exception {
602: // Add some data to the repository
603: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
604: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
605: con.addStatement(picasso, RDF.TYPE, painter, context1);
606: con.addStatement(guernica, RDF.TYPE, painting, context1);
607: con.addStatement(picasso, paints, guernica, context1);
608: con.commit();
609:
610: ParsedTupleQuery tupleQuery = QueryParserUtil.parseTupleQuery(
611: QueryLanguage.SERQL,
612: "select X from {X} rdf:type {Y} rdf:type {rdfs:Class}",
613: null);
614: TupleExpr tupleExpr = tupleQuery.getTupleExpr();
615:
616: MapBindingSet bindings = new MapBindingSet(1);
617:
618: CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
619: iter = con.evaluate(tupleExpr, null, bindings, false);
620: assertEquals(2, countElements(iter));
621:
622: bindings.addBinding("Y", painter);
623: iter = con.evaluate(tupleExpr, null, bindings, false);
624: assertEquals(1, countElements(iter));
625:
626: bindings.addBinding("Z", painting);
627: iter = con.evaluate(tupleExpr, null, bindings, false);
628: assertEquals(1, countElements(iter));
629:
630: bindings.removeBinding("Y");
631: iter = con.evaluate(tupleExpr, null, bindings, false);
632: assertEquals(2, countElements(iter));
633: }
634:
635: public void testMultiThreadedAccess() {
636:
637: Runnable runnable = new Runnable() {
638:
639: SailConnection sharedCon = con;
640:
641: public void run() {
642: assertTrue(sharedCon != null);
643:
644: try {
645: sharedCon.addStatement(painter, RDF.TYPE,
646: RDFS.CLASS);
647: sharedCon.commit();
648:
649: // wait a bit to allow other thread to add stuff as well.
650: Thread.sleep(500L);
651: CloseableIteration<? extends Statement, SailException> result = sharedCon
652: .getStatements(null, null, null, true);
653:
654: assertTrue(result.hasNext());
655: int numberOfStatements = 0;
656: while (result.hasNext()) {
657: numberOfStatements++;
658: Statement st = result.next();
659: assertTrue(st.getSubject().equals(painter)
660: || st.getSubject().equals(picasso));
661: assertTrue(st.getPredicate().equals(RDF.TYPE));
662: assertTrue(st.getObject().equals(RDFS.CLASS)
663: || st.getObject().equals(painter));
664: }
665: assertTrue(
666: "we should have retrieved statements from both threads",
667: numberOfStatements == 2);
668:
669: } catch (SailException e) {
670: e.printStackTrace();
671: fail(e.getMessage());
672: } catch (InterruptedException e) {
673: fail(e.getMessage());
674: }
675:
676: // let this thread sleep so the other thread can invoke close()
677: // first.
678: try {
679: Thread.sleep(1000L);
680:
681: // the connection should now be closed (by the other thread),
682: // invoking any further operation should cause a
683: // IllegalStateException
684: sharedCon.getStatements(null, null, null, true);
685: fail("should have caused an IllegalStateException");
686: } catch (InterruptedException e) {
687: fail(e.getMessage());
688: } catch (SailException e) {
689: e.printStackTrace();
690: fail(e.getMessage());
691: } catch (IllegalStateException e) {
692: // do nothing, this is the expected behaviour
693: }
694: }
695: }; // end anonymous class declaration
696:
697: // execute the other thread
698: Thread newThread = new Thread(runnable, "B (parallel)");
699: newThread.start();
700:
701: try {
702: con.addStatement(picasso, RDF.TYPE, painter);
703: con.commit();
704: // let this thread sleep to enable other thread to finish its business.
705: Thread.sleep(1000L);
706: con.close();
707: } catch (SailException e) {
708: e.printStackTrace();
709: fail(e.getMessage());
710: } catch (InterruptedException e) {
711: fail(e.getMessage());
712: }
713: }
714:
715: public void testStatementEquals() throws Exception {
716: Statement st = vf.createStatement(picasso, RDF.TYPE, painter);
717: assertEquals(st, vf.createStatement(picasso, RDF.TYPE, painter,
718: context1));
719: assertEquals(st, vf.createStatement(picasso, RDF.TYPE, painter,
720: context2));
721: }
722:
723: public void testStatementSerialization() throws Exception {
724: Statement st = vf.createStatement(picasso, RDF.TYPE, painter);
725:
726: ByteArrayOutputStream baos = new ByteArrayOutputStream();
727: ObjectOutputStream out = new ObjectOutputStream(baos);
728: out.writeObject(st);
729: out.close();
730:
731: ByteArrayInputStream bais = new ByteArrayInputStream(baos
732: .toByteArray());
733: ObjectInputStream in = new ObjectInputStream(bais);
734: Statement deserializedStatement = (Statement) in.readObject();
735: in.close();
736:
737: assertTrue(st.equals(deserializedStatement));
738: }
739:
740: public void testGetNamespaces() throws Exception {
741: con.setNamespace("rdf", RDF.NAMESPACE);
742: con.commit();
743:
744: CloseableIteration<? extends Namespace, SailException> namespaces = con
745: .getNamespaces();
746: try {
747: assertTrue(namespaces.hasNext());
748: Namespace rdf = namespaces.next();
749: assertEquals("rdf", rdf.getPrefix());
750: assertEquals(RDF.NAMESPACE, rdf.getName());
751: assertTrue(!namespaces.hasNext());
752: } finally {
753: namespaces.close();
754: }
755: }
756:
757: public void testGetNamespace() throws Exception {
758: con.setNamespace("rdf", RDF.NAMESPACE);
759: con.commit();
760: assertEquals(RDF.NAMESPACE, con.getNamespace("rdf"));
761: }
762:
763: public void testClearNamespaces() throws Exception {
764: con.setNamespace("rdf", RDF.NAMESPACE);
765: con.setNamespace("rdfs", RDFS.NAMESPACE);
766: con.clearNamespaces();
767: con.commit();
768: assertTrue(!con.getNamespaces().hasNext());
769: }
770:
771: public void testRemoveNamespaces() throws Exception {
772: con.setNamespace("rdf", RDF.NAMESPACE);
773: con.removeNamespace("rdf");
774: con.commit();
775: assertNull(con.getNamespace("rdf"));
776: }
777:
778: public void testGetContextIDs() throws Exception {
779: assertEquals(0, countElements(con.getContextIDs()));
780:
781: // load data
782: con.addStatement(picasso, paints, guernica, context1);
783: assertEquals(1, countElements(con.getContextIDs()));
784: assertEquals(context1, first(con.getContextIDs()));
785:
786: con.removeStatements(picasso, paints, guernica, context1);
787: assertEquals(0, countElements(con.getContextIDs()));
788: con.commit();
789:
790: assertEquals(0, countElements(con.getContextIDs()));
791:
792: con.addStatement(picasso, paints, guernica, context2);
793: assertEquals(1, countElements(con.getContextIDs()));
794: assertEquals(context2, first(con.getContextIDs()));
795: }
796:
797: public void testOldURI() throws Exception {
798: assertEquals(0, countAllElements());
799: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
800: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
801: con.addStatement(picasso, RDF.TYPE, painter, context1);
802: con.addStatement(guernica, RDF.TYPE, painting, context1);
803: con.addStatement(picasso, paints, guernica, context1);
804: assertEquals(5, countAllElements());
805: con.commit();
806: con.clear();
807: con.commit();
808: con.addStatement(picasso, paints, guernica, context1);
809: con.commit();
810: assertEquals(1, countAllElements());
811: }
812:
813: public void testDualConnections() throws Exception {
814: SailConnection con2 = sail.getConnection();
815: try {
816: assertEquals(0, countAllElements());
817: con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
818: con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
819: con.addStatement(picasso, RDF.TYPE, painter, context1);
820: con.addStatement(guernica, RDF.TYPE, painting, context1);
821: con.commit();
822: assertEquals(4, countAllElements());
823: con2.addStatement(RDF.NIL, RDF.TYPE, RDF.LIST);
824: String query = "SELECT S, P, O FROM {S} P {O}";
825: ParsedTupleQuery tupleQuery = QueryParserUtil
826: .parseTupleQuery(QueryLanguage.SERQL, query, null);
827: assertEquals(5, countElements(con2.evaluate(tupleQuery
828: .getTupleExpr(), null, EmptyBindingSet
829: .getInstance(), false)));
830: Runnable clearer = new Runnable() {
831: public void run() {
832: try {
833: con.clear();
834: con.commit();
835: } catch (SailException e) {
836: throw new RuntimeException(e);
837: }
838: }
839: };
840: Thread thread = new Thread(clearer);
841: thread.start();
842: Thread.yield();
843: Thread.yield();
844: con2.commit();
845: thread.join();
846: } finally {
847: con2.close();
848: }
849: }
850:
851: public void sailChanged(SailChangedEvent event) {
852: if (event.statementsAdded()) {
853: addEventCount++;
854: }
855: if (event.statementsRemoved()) {
856: removeEventCount++;
857: }
858: }
859:
860: private <T> T first(Iteration<T, ?> iter) throws Exception {
861: try {
862: if (iter.hasNext()) {
863: return iter.next();
864: }
865: } finally {
866: Iterations.closeCloseable(iter);
867: }
868:
869: return null;
870: }
871:
872: private int countContext1Elements() throws Exception, SailException {
873: return countElements(con.getStatements(null, null, null, false,
874: context1));
875: }
876:
877: private int countAllElements() throws Exception, SailException {
878: return countElements(con.getStatements(null, null, null, false));
879: }
880:
881: private int countElements(Iteration<?, ?> iter) throws Exception {
882: int count = 0;
883:
884: try {
885: while (iter.hasNext()) {
886: iter.next();
887: count++;
888: }
889: } finally {
890: Iterations.closeCloseable(iter);
891: }
892:
893: return count;
894: }
895:
896: private int countQueryResults(String query) throws Exception {
897: ParsedTupleQuery tupleQuery = QueryParserUtil.parseTupleQuery(
898: QueryLanguage.SERQL, query + " using namespace ex = <"
899: + EXAMPLE_NS + ">", null);
900:
901: return countElements(con.evaluate(tupleQuery.getTupleExpr(),
902: null, EmptyBindingSet.getInstance(), false));
903: }
904: }
|