001package systems.dmx.core.impl; 002 003import systems.dmx.core.model.AssociationModel; 004import systems.dmx.core.model.DMXObjectModel; 005import systems.dmx.core.model.IndexMode; 006import systems.dmx.core.model.RelatedAssociationModel; 007import systems.dmx.core.model.RelatedTopicModel; 008import systems.dmx.core.model.SimpleValue; 009import systems.dmx.core.model.TopicModel; 010import systems.dmx.core.service.ModelFactory; 011import systems.dmx.core.storage.spi.DMXTransaction; 012import systems.dmx.core.storage.spi.DMXStorage; 013 014import static java.util.Arrays.asList; 015import java.util.ArrayList; 016import java.util.Iterator; 017import java.util.List; 018import java.util.logging.Logger; 019 020 021 022/** 023 * A thin convenience layer above vendor specific storage. 024 * 2 responsibilites: 025 * - Adapts public storage API to Core internal API (type casting). 026 * - Adds fetch-single calls on top of fetch-multiple calls and performs sanity checks. 027 */ 028class StorageDecorator { 029 030 // ---------------------------------------------------------------------------------------------- Instance Variables 031 032 private final DMXStorage storage; 033 034 private final Logger logger = Logger.getLogger(getClass().getName()); 035 036 // ---------------------------------------------------------------------------------------------------- Constructors 037 038 StorageDecorator(DMXStorage storage) { 039 this.storage = storage; 040 } 041 042 // ----------------------------------------------------------------------------------------- Package Private Methods 043 044 045 046 // === Topics === 047 048 /** 049 * @return The fetched topic. 050 * Note: its child topics are not fetched. 051 */ 052 final TopicModelImpl fetchTopic(long topicId) { 053 return (TopicModelImpl) storage.fetchTopic(topicId); 054 } 055 056 /** 057 * Looks up a single topic by exact value. 058 * <p> 059 * IMPORTANT: Looking up a topic this way requires the corresponding type to be indexed with indexing mode 060 * <code>dmx.core.key</code>. 061 * 062 * @return The fetched topic, or <code>null</code> if no such topic exists. 063 * Note: its child topics are not fetched. 064 * 065 * @throws RuntimeException if more than one topic is found. 066 */ 067 final TopicModelImpl fetchTopic(String key, SimpleValue value) { 068 return (TopicModelImpl) storage.fetchTopic(key, value.value()); 069 } 070 071 final List<TopicModelImpl> fetchTopics(String key, SimpleValue value) { 072 return (List<TopicModelImpl>) storage.fetchTopics(key, value.value()); 073 } 074 075 // --- 076 077 /** 078 * @return The fetched topics. 079 * Note: their child topics are not fetched. 080 */ 081 final List<TopicModelImpl> queryTopics(String key, SimpleValue value) { 082 return (List<TopicModelImpl>) storage.queryTopics(key, value.value()); 083 } 084 085 // --- 086 087 final Iterator<TopicModelImpl> fetchAllTopics() { 088 return (Iterator<TopicModelImpl>) storage.fetchAllTopics(); 089 } 090 091 // --- 092 093 /** 094 * Creates a topic. 095 * <p> 096 * Actually only the topic URI and type URI are stored and indexed. 097 * The topic value is not stored. 098 * 099 * @return FIXDOC ### the created topic. Note: 100 * - the topic URI is initialzed and persisted. 101 * - the topic value is initialzed but not persisted. 102 * - the type URI is initialzed but not persisted. 103 */ 104 final void storeTopic(TopicModel model) { 105 storage.storeTopic(model); 106 } 107 108 /** 109 * Stores and indexes the topic's URI. 110 */ 111 final void storeTopicUri(long topicId, String uri) { 112 storage.storeTopicUri(topicId, uri); 113 } 114 115 final void storeTopicTypeUri(long topicId, String topicTypeUri) { 116 storage.storeTopicTypeUri(topicId, topicTypeUri); 117 } 118 119 // --- 120 121 /** 122 * Convenience method (no indexing). 123 */ 124 final void storeTopicValue(long topicId, SimpleValue value) { 125 storeTopicValue(topicId, value, asList(IndexMode.OFF), null, null); 126 } 127 128 /** 129 * Stores and indexes the topic's value. ### TODO: separate storing/indexing? 130 */ 131 final void storeTopicValue(long topicId, SimpleValue value, List<IndexMode> indexModes, String indexKey, 132 SimpleValue indexValue) { 133 storage.storeTopicValue(topicId, value, indexModes, indexKey, indexValue); 134 } 135 136 final void indexTopicValue(long topicId, IndexMode indexMode, String indexKey, SimpleValue indexValue) { 137 storage.indexTopicValue(topicId, indexMode, indexKey, indexValue); 138 } 139 140 // --- 141 142 /** 143 * Deletes the topic. 144 * <p> 145 * Prerequisite: the topic has no relations. 146 */ 147 final void _deleteTopic(long topicId) { 148 storage.deleteTopic(topicId); 149 } 150 151 152 153 // === Associations === 154 155 final AssociationModelImpl fetchAssociation(long assocId) { 156 return (AssociationModelImpl) storage.fetchAssociation(assocId); 157 } 158 159 /** 160 * Looks up a single association by exact value. 161 * If no such association exists <code>null</code> is returned. 162 * If more than one association is found a runtime exception is thrown. 163 * <p> 164 * IMPORTANT: Looking up an association this way requires the corresponding type to be indexed with indexing mode 165 * <code>dmx.core.key</code>. 166 * 167 * @return The fetched association. 168 * Note: its child topics are not fetched. 169 */ 170 final AssociationModelImpl fetchAssociation(String key, SimpleValue value) { 171 return (AssociationModelImpl) storage.fetchAssociation(key, value.value()); 172 } 173 174 final List<AssociationModelImpl> fetchAssociations(String key, SimpleValue value) { 175 return (List<AssociationModelImpl>) storage.fetchAssociations(key, value.value()); 176 } 177 178 // --- 179 180 /** 181 * Convenience method (checks singularity). 182 * 183 * Returns the association between two topics, qualified by association type and both role types. 184 * If no such association exists <code>null</code> is returned. 185 * If more than one association exist, a runtime exception is thrown. 186 * 187 * @param assocTypeUri Association type filter. Pass <code>null</code> to switch filter off. 188 * ### FIXME: for methods with a singular return value all filters should be mandatory 189 */ 190 final AssociationModelImpl fetchAssociation(String assocTypeUri, long topicId1, long topicId2, 191 String roleTypeUri1, String roleTypeUri2) { 192 List<AssociationModelImpl> assocs = fetchAssociations(assocTypeUri, topicId1, topicId2, roleTypeUri1, 193 roleTypeUri2); 194 switch (assocs.size()) { 195 case 0: 196 return null; 197 case 1: 198 return assocs.get(0); 199 default: 200 throw new RuntimeException("Ambiguity: there are " + assocs.size() + " \"" + assocTypeUri + 201 "\" associations (topicId1=" + topicId1 + ", topicId2=" + topicId2 + ", " + 202 "roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\")"); 203 } 204 } 205 206 /** 207 * Returns the associations between two topics. If no such association exists an empty set is returned. 208 * 209 * @param assocTypeUri Association type filter. Pass <code>null</code> to switch filter off. 210 */ 211 final List<AssociationModelImpl> fetchAssociations(String assocTypeUri, long topicId1, long topicId2, 212 String roleTypeUri1, String roleTypeUri2) { 213 return (List<AssociationModelImpl>) storage.fetchAssociations(assocTypeUri, topicId1, topicId2, roleTypeUri1, 214 roleTypeUri2); 215 } 216 217 // --- 218 219 /** 220 * Convenience method (checks singularity). 221 */ 222 final AssociationModelImpl fetchAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, 223 long assocId, String topicRoleTypeUri, String assocRoleTypeUri) { 224 List<AssociationModelImpl> assocs = fetchAssociationsBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, 225 topicRoleTypeUri, assocRoleTypeUri); 226 switch (assocs.size()) { 227 case 0: 228 return null; 229 case 1: 230 return assocs.get(0); 231 default: 232 throw new RuntimeException("Ambiguity: there are " + assocs.size() + " \"" + assocTypeUri + 233 "\" associations (topicId=" + topicId + ", assocId=" + assocId + ", " + 234 "topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\")"); 235 } 236 } 237 238 final List<AssociationModelImpl> fetchAssociationsBetweenTopicAndAssociation(String assocTypeUri, long topicId, 239 long assocId, String topicRoleTypeUri, String assocRoleTypeUri) { 240 return (List<AssociationModelImpl>) storage.fetchAssociationsBetweenTopicAndAssociation(assocTypeUri, topicId, 241 assocId, topicRoleTypeUri, assocRoleTypeUri); 242 } 243 244 // --- 245 246 final Iterator<AssociationModelImpl> fetchAllAssociations() { 247 return (Iterator<AssociationModelImpl>) storage.fetchAllAssociations(); 248 } 249 250 final long[] fetchPlayerIds(long assocId) { 251 return storage.fetchPlayerIds(assocId); 252 } 253 254 // --- 255 256 /** 257 * Stores and indexes the association's URI. 258 */ 259 final void storeAssociationUri(long assocId, String uri) { 260 storage.storeAssociationUri(assocId, uri); 261 } 262 263 final void storeAssociationTypeUri(long assocId, String assocTypeUri) { 264 storage.storeAssociationTypeUri(assocId, assocTypeUri); 265 } 266 267 final void storeRoleTypeUri(long assocId, long playerId, String roleTypeUri) { 268 storage.storeRoleTypeUri(assocId, playerId, roleTypeUri); 269 } 270 271 // --- 272 273 /** 274 * Convenience method (no indexing). 275 */ 276 final void storeAssociationValue(long assocId, SimpleValue value) { 277 storeAssociationValue(assocId, value, asList(IndexMode.OFF), null, null); 278 } 279 280 /** 281 * Stores and indexes the association's value. ### TODO: separate storing/indexing? 282 */ 283 final void storeAssociationValue(long assocId, SimpleValue value, List<IndexMode> indexModes, String indexKey, 284 SimpleValue indexValue) { 285 storage.storeAssociationValue(assocId, value, indexModes, indexKey, indexValue); 286 } 287 288 final void indexAssociationValue(long assocId, IndexMode indexMode, String indexKey, SimpleValue indexValue) { 289 storage.indexAssociationValue(assocId, indexMode, indexKey, indexValue); 290 } 291 292 // --- 293 294 final void storeAssociation(AssociationModel model) { 295 storage.storeAssociation(model); 296 } 297 298 final void _deleteAssociation(long assocId) { 299 storage.deleteAssociation(assocId); 300 } 301 302 303 304 // === Generic Object === 305 306 final DMXObjectModelImpl fetchObject(long id) { 307 return (DMXObjectModelImpl) storage.fetchObject(id); 308 } 309 310 311 312 // === Traversal === 313 314 // --- Topic Source --- 315 316 /** 317 * Convenience method (checks singularity). 318 * 319 * @param assocTypeUri may be null 320 * @param myRoleTypeUri may be null 321 * @param othersRoleTypeUri may be null 322 * @param othersTopicTypeUri may be null 323 * 324 * @return The fetched topic. 325 * Note: its child topics are not fetched. 326 */ 327 final RelatedTopicModelImpl fetchTopicRelatedTopic(long topicId, String assocTypeUri, String myRoleTypeUri, 328 String othersRoleTypeUri, String othersTopicTypeUri) { 329 List<RelatedTopicModelImpl> topics = fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri, 330 othersRoleTypeUri, othersTopicTypeUri); 331 switch (topics.size()) { 332 case 0: 333 return null; 334 case 1: 335 return topics.iterator().next(); 336 default: 337 throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (topicId=" + 338 topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " + 339 "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")"); 340 } 341 } 342 343 /** 344 * @param assocTypeUri may be null 345 * @param myRoleTypeUri may be null 346 * @param othersRoleTypeUri may be null 347 * @param othersTopicTypeUri may be null 348 * 349 * @return The fetched topics. 350 * Note: their child topics are not fetched. 351 */ 352 final List<RelatedTopicModelImpl> fetchTopicRelatedTopics(long topicId, String assocTypeUri, String myRoleTypeUri, 353 String othersRoleTypeUri, String othersTopicTypeUri) { 354 return (List<RelatedTopicModelImpl>) storage.fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri, 355 othersRoleTypeUri, othersTopicTypeUri); 356 } 357 358 /** 359 * Convenience method (receives *list* of association types). 360 * 361 * @param assocTypeUris may *not* be null 362 * @param myRoleTypeUri may be null 363 * @param othersRoleTypeUri may be null 364 * @param othersTopicTypeUri may be null 365 * 366 * @return The fetched topics. 367 * Note: their child topics are not fetched. 368 */ 369 final List<RelatedTopicModelImpl> fetchTopicRelatedTopics(long topicId, List<String> assocTypeUris, 370 String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) { 371 List<RelatedTopicModelImpl> result = new ArrayList(); 372 for (String assocTypeUri : assocTypeUris) { 373 result.addAll(fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri, othersRoleTypeUri, 374 othersTopicTypeUri)); 375 } 376 return result; 377 } 378 379 // --- 380 381 /** 382 * Convenience method (checks singularity). 383 * 384 * @return The fetched association. 385 * Note: its child topics are not fetched. 386 */ 387 final RelatedAssociationModelImpl fetchTopicRelatedAssociation(long topicId, String assocTypeUri, 388 String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) { 389 List<RelatedAssociationModelImpl> assocs = fetchTopicRelatedAssociations(topicId, assocTypeUri, myRoleTypeUri, 390 othersRoleTypeUri, othersAssocTypeUri); 391 switch (assocs.size()) { 392 case 0: 393 return null; 394 case 1: 395 return assocs.iterator().next(); 396 default: 397 throw new RuntimeException("Ambiguity: there are " + assocs.size() + " related associations (topicId=" + 398 topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " + 399 "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri + "\")"); 400 } 401 } 402 403 /** 404 * @param assocTypeUri may be null 405 * @param myRoleTypeUri may be null 406 * @param othersRoleTypeUri may be null 407 * @param othersAssocTypeUri may be null 408 * 409 * @return The fetched associations. 410 * Note: their child topics are not fetched. 411 */ 412 final List<RelatedAssociationModelImpl> fetchTopicRelatedAssociations(long topicId, String assocTypeUri, 413 String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) { 414 return (List<RelatedAssociationModelImpl>) storage.fetchTopicRelatedAssociations(topicId, assocTypeUri, 415 myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri); 416 } 417 418 // --- 419 420 /** 421 * @return The fetched associations. 422 * Note: their child topics are not fetched. 423 */ 424 final List<AssociationModelImpl> fetchTopicAssociations(long topicId) { 425 return (List<AssociationModelImpl>) storage.fetchTopicAssociations(topicId); 426 } 427 428 // --- Association Source --- 429 430 /** 431 * Convenience method (checks singularity). 432 * 433 * @return The fetched topic. 434 * Note: its child topics are not fetched. 435 */ 436 final RelatedTopicModelImpl fetchAssociationRelatedTopic(long assocId, String assocTypeUri, String myRoleTypeUri, 437 String othersRoleTypeUri, String othersTopicTypeUri) { 438 List<RelatedTopicModelImpl> topics = fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri, 439 othersRoleTypeUri, othersTopicTypeUri); 440 switch (topics.size()) { 441 case 0: 442 return null; 443 case 1: 444 return topics.iterator().next(); 445 default: 446 throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (assocId=" + 447 assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " + 448 "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")"); 449 } 450 } 451 452 /** 453 * @return The fetched topics. 454 * Note: their child topics are not fetched. 455 */ 456 final List<RelatedTopicModelImpl> fetchAssociationRelatedTopics(long assocId, String assocTypeUri, 457 String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) { 458 return (List<RelatedTopicModelImpl>) storage.fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri, 459 othersRoleTypeUri, othersTopicTypeUri); 460 } 461 462 /** 463 * Convenience method (receives *list* of association types). 464 * 465 * @param assocTypeUris may be null 466 * @param myRoleTypeUri may be null 467 * @param othersRoleTypeUri may be null 468 * @param othersTopicTypeUri may be null 469 * 470 * @return The fetched topics. 471 * Note: their child topics are not fetched. 472 */ 473 final List<RelatedTopicModelImpl> fetchAssociationRelatedTopics(long assocId, List<String> assocTypeUris, 474 String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) { 475 List<RelatedTopicModelImpl> result = new ArrayList(); 476 for (String assocTypeUri : assocTypeUris) { 477 result.addAll(fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri, othersRoleTypeUri, 478 othersTopicTypeUri)); 479 } 480 return result; 481 } 482 483 // --- 484 485 /** 486 * Convenience method (checks singularity). 487 * 488 * @return The fetched association. 489 * Note: its child topics are not fetched. 490 */ 491 final RelatedAssociationModelImpl fetchAssociationRelatedAssociation(long assocId, String assocTypeUri, 492 String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) { 493 List<RelatedAssociationModelImpl> assocs = fetchAssociationRelatedAssociations(assocId, assocTypeUri, 494 myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri); 495 switch (assocs.size()) { 496 case 0: 497 return null; 498 case 1: 499 return assocs.iterator().next(); 500 default: 501 throw new RuntimeException("Ambiguity: there are " + assocs.size() + " related associations (assocId=" + 502 assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " + 503 "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri + 504 "\"),\nresult=" + assocs); 505 } 506 } 507 508 /** 509 * @param assocTypeUri may be null 510 * @param myRoleTypeUri may be null 511 * @param othersRoleTypeUri may be null 512 * @param othersAssocTypeUri may be null 513 * 514 * @return The fetched associations. 515 * Note: their child topics are not fetched. 516 */ 517 final List<RelatedAssociationModelImpl> fetchAssociationRelatedAssociations(long assocId, String assocTypeUri, 518 String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) { 519 return (List<RelatedAssociationModelImpl>) storage.fetchAssociationRelatedAssociations(assocId, assocTypeUri, 520 myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri); 521 } 522 523 // --- 524 525 final List<AssociationModelImpl> fetchAssociationAssociations(long assocId) { 526 return (List<AssociationModelImpl>) storage.fetchAssociationAssociations(assocId); 527 } 528 529 // --- Object Source --- 530 531 /** 532 * Convenience method (checks singularity). 533 * 534 * @param objectId id of a topic or an association 535 * @param assocTypeUri may be null 536 * @param myRoleTypeUri may be null 537 * @param othersRoleTypeUri may be null 538 * @param othersTopicTypeUri may be null 539 * 540 * @return The fetched topic. 541 * Note: its child topics are not fetched. 542 */ 543 final RelatedTopicModelImpl fetchRelatedTopic(long objectId, String assocTypeUri, String myRoleTypeUri, 544 String othersRoleTypeUri, String othersTopicTypeUri) { 545 List<RelatedTopicModelImpl> topics = fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri, 546 othersRoleTypeUri, othersTopicTypeUri); 547 switch (topics.size()) { 548 case 0: 549 return null; 550 case 1: 551 return topics.iterator().next(); 552 default: 553 throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (objectId=" + 554 objectId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " + 555 "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")"); 556 } 557 } 558 559 /** 560 * @param objectId id of a topic or an association 561 * @param assocTypeUri may be null 562 * @param myRoleTypeUri may be null 563 * @param othersRoleTypeUri may be null 564 * @param othersTopicTypeUri may be null 565 * 566 * @return The fetched topics. 567 * Note: their child topics are not fetched. 568 */ 569 final List<RelatedTopicModelImpl> fetchRelatedTopics(long objectId, String assocTypeUri, String myRoleTypeUri, 570 String othersRoleTypeUri, String othersTopicTypeUri) { 571 return (List<RelatedTopicModelImpl>) storage.fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri, 572 othersRoleTypeUri, othersTopicTypeUri); 573 } 574 575 // ### TODO: decorator for fetchRelatedAssociations() 576 577 578 579 // === Properties === 580 581 final Object fetchProperty(long id, String propUri) { 582 return storage.fetchProperty(id, propUri); 583 } 584 585 final boolean hasProperty(long id, String propUri) { 586 return storage.hasProperty(id, propUri); 587 } 588 589 // --- 590 591 final List<TopicModelImpl> fetchTopicsByProperty(String propUri, Object propValue) { 592 return (List<TopicModelImpl>) storage.fetchTopicsByProperty(propUri, propValue); 593 } 594 595 final List<TopicModelImpl> fetchTopicsByPropertyRange(String propUri, Number from, Number to) { 596 return (List<TopicModelImpl>) storage.fetchTopicsByPropertyRange(propUri, from, to); 597 } 598 599 final List<AssociationModelImpl> fetchAssociationsByProperty(String propUri, Object propValue) { 600 return (List<AssociationModelImpl>) storage.fetchAssociationsByProperty(propUri, propValue); 601 } 602 603 final List<AssociationModelImpl> fetchAssociationsByPropertyRange(String propUri, Number from, Number to) { 604 return (List<AssociationModelImpl>) storage.fetchAssociationsByPropertyRange(propUri, from, to); 605 } 606 607 // --- 608 609 final void storeTopicProperty(long topicId, String propUri, Object propValue, boolean addToIndex) { 610 storage.storeTopicProperty(topicId, propUri, propValue, addToIndex); 611 } 612 613 final void storeAssociationProperty(long assocId, String propUri, Object propValue, boolean addToIndex) { 614 storage.storeAssociationProperty(assocId, propUri, propValue, addToIndex); 615 } 616 617 // --- 618 619 final void indexTopicProperty(long topicId, String propUri, Object propValue) { 620 storage.indexTopicProperty(topicId, propUri, propValue); 621 } 622 623 final void indexAssociationProperty(long assocId, String propUri, Object propValue) { 624 storage.indexAssociationProperty(assocId, propUri, propValue); 625 } 626 627 // --- 628 629 final void removeTopicProperty(long topicId, String propUri) { 630 storage.deleteTopicProperty(topicId, propUri); 631 } 632 633 final void removeAssociationProperty(long assocId, String propUri) { 634 storage.deleteAssociationProperty(assocId, propUri); 635 } 636 637 638 639 // === DB === 640 641 final DMXTransaction beginTx() { 642 return storage.beginTx(); 643 } 644 645 /** 646 * Initializes the database. 647 * Prerequisite: there is an open transaction. 648 * 649 * @return <code>true</code> if a clean install is detected, <code>false</code> otherwise. 650 */ 651 final boolean init() { 652 boolean isCleanInstall = storage.setupRootNode(); 653 if (isCleanInstall) { 654 logger.info("Clean install detected -- Starting with a fresh DB"); 655 storeMigrationNr(0); 656 } 657 return isCleanInstall; 658 } 659 660 final void shutdown() { 661 storage.shutdown(); 662 } 663 664 // --- 665 666 final int fetchMigrationNr() { 667 return (Integer) fetchProperty(0, "core_migration_nr"); 668 } 669 670 final void storeMigrationNr(int migrationNr) { 671 storage.storeTopicProperty(0, "core_migration_nr", migrationNr, false); // addToIndex=false 672 } 673 674 // --- 675 676 final Object getDatabaseVendorObject() { 677 return storage.getDatabaseVendorObject(); 678 } 679 680 final Object getDatabaseVendorObject(long objectId) { 681 return storage.getDatabaseVendorObject(objectId); 682 } 683}