001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.AssociationDefinition; 005 import de.deepamehta.core.AssociationType; 006 import de.deepamehta.core.RelatedAssociation; 007 import de.deepamehta.core.RelatedTopic; 008 import de.deepamehta.core.Topic; 009 import de.deepamehta.core.TopicType; 010 import de.deepamehta.core.Type; 011 import de.deepamehta.core.model.AssociationModel; 012 import de.deepamehta.core.model.AssociationRoleModel; 013 import de.deepamehta.core.model.AssociationTypeModel; 014 import de.deepamehta.core.model.DeepaMehtaObjectModel; 015 import de.deepamehta.core.model.RelatedAssociationModel; 016 import de.deepamehta.core.model.RelatedTopicModel; 017 import de.deepamehta.core.model.RoleModel; 018 import de.deepamehta.core.model.SimpleValue; 019 import de.deepamehta.core.model.TopicModel; 020 import de.deepamehta.core.model.TopicRoleModel; 021 import de.deepamehta.core.model.TopicTypeModel; 022 import de.deepamehta.core.service.DeepaMehtaEvent; 023 import de.deepamehta.core.service.DeepaMehtaService; 024 import de.deepamehta.core.service.Plugin; 025 import de.deepamehta.core.service.PluginInfo; 026 import de.deepamehta.core.service.ResultList; 027 import de.deepamehta.core.service.TypeStorage; 028 import de.deepamehta.core.storage.spi.DeepaMehtaTransaction; 029 030 import org.osgi.framework.BundleContext; 031 032 import java.util.ArrayList; 033 import java.util.List; 034 import java.util.logging.Logger; 035 036 037 038 /** 039 * Implementation of the DeepaMehta core service. Embeddable into Java applications. 040 */ 041 public class EmbeddedService implements DeepaMehtaService { 042 043 // ------------------------------------------------------------------------------------------------------- Constants 044 045 private static final String URI_PREFIX_TOPIC_TYPE = "domain.project.topic_type_"; 046 private static final String URI_PREFIX_ASSOCIATION_TYPE = "domain.project.assoc_type_"; 047 private static final String URI_PREFIX_ROLE_TYPE = "domain.project.role_type_"; 048 049 // ---------------------------------------------------------------------------------------------- Instance Variables 050 051 StorageDecorator storageDecorator; 052 BundleContext bundleContext; 053 MigrationManager migrationManager; 054 PluginManager pluginManager; 055 EventManager eventManager; 056 TypeCache typeCache; 057 TypeStorageImpl typeStorage; 058 ValueStorage valueStorage; 059 060 private Logger logger = Logger.getLogger(getClass().getName()); 061 062 // ---------------------------------------------------------------------------------------------------- Constructors 063 064 /** 065 * @param bundleContext The context of the DeepaMehta 4 Core bundle. 066 */ 067 public EmbeddedService(StorageDecorator storageDecorator, BundleContext bundleContext) { 068 this.storageDecorator = storageDecorator; 069 this.bundleContext = bundleContext; 070 this.migrationManager = new MigrationManager(this); 071 this.pluginManager = new PluginManager(this); 072 this.eventManager = new EventManager(this); 073 this.typeCache = new TypeCache(this); 074 this.typeStorage = new TypeStorageImpl(this); 075 this.valueStorage = new ValueStorage(this); 076 bootstrapTypeCache(); 077 setupDB(); 078 } 079 080 // -------------------------------------------------------------------------------------------------- Public Methods 081 082 083 084 // **************************************** 085 // *** DeepaMehtaService Implementation *** 086 // **************************************** 087 088 089 090 // === Topics === 091 092 @Override 093 public Topic getTopic(long topicId) { 094 try { 095 return instantiateTopic(storageDecorator.fetchTopic(topicId)); 096 } catch (Exception e) { 097 throw new RuntimeException("Fetching topic " + topicId + " failed", e); 098 } 099 } 100 101 @Override 102 public Topic getTopic(String key, SimpleValue value) { 103 try { 104 TopicModel topic = storageDecorator.fetchTopic(key, value); 105 return topic != null ? instantiateTopic(topic) : null; 106 } catch (Exception e) { 107 throw new RuntimeException("Fetching topic failed (key=\"" + key + "\", value=\"" + value + "\")", e); 108 } 109 } 110 111 @Override 112 public List<Topic> getTopics(String key, SimpleValue value) { 113 try { 114 return instantiateTopics(storageDecorator.fetchTopics(key, value)); 115 } catch (Exception e) { 116 throw new RuntimeException("Fetching topics failed (key=\"" + key + "\", value=\"" + value + "\")", e); 117 } 118 } 119 120 @Override 121 public ResultList<RelatedTopic> getTopics(String topicTypeUri, int maxResultSize) { 122 try { 123 return getTopicType(topicTypeUri).getRelatedTopics("dm4.core.instantiation", "dm4.core.type", 124 "dm4.core.instance", topicTypeUri, maxResultSize); 125 } catch (Exception e) { 126 throw new RuntimeException("Fetching topics by type failed (topicTypeUri=\"" + topicTypeUri + "\")", e); 127 } 128 } 129 130 @Override 131 public List<Topic> searchTopics(String searchTerm, String fieldUri) { 132 try { 133 return instantiateTopics(storageDecorator.queryTopics(searchTerm, fieldUri)); 134 } catch (Exception e) { 135 throw new RuntimeException("Searching topics failed (searchTerm=\"" + searchTerm + "\", fieldUri=\"" + 136 fieldUri + "\")", e); 137 } 138 } 139 140 @Override 141 public Iterable<Topic> getAllTopics() { 142 return new TopicIterable(this); 143 } 144 145 // --- 146 147 @Override 148 public Topic createTopic(TopicModel model) { 149 return createTopic(model, null); // uriPrefix=null 150 } 151 152 @Override 153 public void updateTopic(TopicModel model) { 154 try { 155 getTopic(model.getId()).update(model); 156 } catch (Exception e) { 157 throw new RuntimeException("Updating topic failed (" + model + ")", e); 158 } 159 } 160 161 @Override 162 public void deleteTopic(long topicId) { 163 try { 164 getTopic(topicId).delete(); 165 } catch (Exception e) { 166 throw new RuntimeException("Deleting topic " + topicId + " failed", e); 167 } 168 } 169 170 171 172 // === Associations === 173 174 @Override 175 public Association getAssociation(long assocId) { 176 logger.info("assocId=" + assocId); 177 try { 178 return instantiateAssociation(storageDecorator.fetchAssociation(assocId)); 179 } catch (Exception e) { 180 throw new RuntimeException("Fetching association " + assocId + " failed", e); 181 } 182 } 183 184 @Override 185 public Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id, 186 String roleTypeUri1, String roleTypeUri2) { 187 String info = "assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 188 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""; 189 try { 190 AssociationModel assoc = storageDecorator.fetchAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, 191 roleTypeUri2); 192 return assoc != null ? instantiateAssociation(assoc) : null; 193 } catch (Exception e) { 194 throw new RuntimeException("Fetching association failed (" + info + ")", e); 195 } 196 } 197 198 @Override 199 public Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId, 200 String topicRoleTypeUri, String assocRoleTypeUri) { 201 String info = "assocTypeUri=\"" + assocTypeUri + "\", topicId=" + topicId + ", assocId=" + assocId + 202 ", topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\""; 203 logger.info(info); 204 try { 205 AssociationModel assoc = storageDecorator.fetchAssociationBetweenTopicAndAssociation(assocTypeUri, 206 topicId, assocId, topicRoleTypeUri, assocRoleTypeUri); 207 return assoc != null ? instantiateAssociation(assoc) : null; 208 } catch (Exception e) { 209 throw new RuntimeException("Fetching association failed (" + info + ")", e); 210 } 211 } 212 213 // --- 214 215 @Override 216 public ResultList<RelatedAssociation> getAssociations(String assocTypeUri) { 217 try { 218 return getAssociationType(assocTypeUri).getRelatedAssociations("dm4.core.instantiation", 219 "dm4.core.type", "dm4.core.instance", assocTypeUri); 220 } catch (Exception e) { 221 throw new RuntimeException("Fetching associations by type failed (assocTypeUri=\"" + assocTypeUri + "\")", 222 e); 223 } 224 } 225 226 @Override 227 public List<Association> getAssociations(long topic1Id, long topic2Id) { 228 return getAssociations(topic1Id, topic2Id, null); 229 } 230 231 @Override 232 public List<Association> getAssociations(long topic1Id, long topic2Id, String assocTypeUri) { 233 logger.info("topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + ", assocTypeUri=\"" + assocTypeUri + "\""); 234 try { 235 return instantiateAssociations(storageDecorator.fetchAssociations(assocTypeUri, topic1Id, topic2Id, 236 null, null)); // roleTypeUri1=null, roleTypeUri2=null 237 } catch (Exception e) { 238 throw new RuntimeException("Fetching associations between topics " + topic1Id + " and " + topic2Id + 239 " failed (assocTypeUri=\"" + assocTypeUri + "\")", e); 240 } 241 } 242 243 // --- 244 245 @Override 246 public Iterable<Association> getAllAssociations() { 247 return new AssociationIterable(this); 248 } 249 250 // --- 251 252 @Override 253 public Association createAssociation(AssociationModel model) { 254 try { 255 fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION, model); 256 Association assoc = associationFactory(model); 257 fireEvent(CoreEvent.POST_CREATE_ASSOCIATION, assoc); 258 return assoc; 259 } catch (Exception e) { 260 throw new RuntimeException("Creating association failed (" + model + ")", e); 261 } 262 } 263 264 @Override 265 public void updateAssociation(AssociationModel model) { 266 try { 267 getAssociation(model.getId()).update(model); 268 } catch (Exception e) { 269 throw new RuntimeException("Updating association failed (" + model + ")", e); 270 } 271 } 272 273 @Override 274 public void deleteAssociation(long assocId) { 275 try { 276 getAssociation(assocId).delete(); 277 } catch (Exception e) { 278 throw new RuntimeException("Deleting association " + assocId + " failed", e); 279 } 280 } 281 282 283 284 // === Topic Types === 285 286 @Override 287 public List<String> getTopicTypeUris() { 288 try { 289 Topic metaType = instantiateTopic(storageDecorator.fetchTopic("uri", 290 new SimpleValue("dm4.core.topic_type"))); 291 ResultList<RelatedTopic> topicTypes = metaType.getRelatedTopics("dm4.core.instantiation", "dm4.core.type", 292 "dm4.core.instance", "dm4.core.topic_type", 0); 293 List<String> topicTypeUris = new ArrayList(); 294 // add meta types 295 topicTypeUris.add("dm4.core.topic_type"); 296 topicTypeUris.add("dm4.core.assoc_type"); 297 topicTypeUris.add("dm4.core.meta_type"); 298 topicTypeUris.add("dm4.core.meta_meta_type"); 299 // add regular types 300 for (Topic topicType : topicTypes) { 301 topicTypeUris.add(topicType.getUri()); 302 } 303 return topicTypeUris; 304 } catch (Exception e) { 305 throw new RuntimeException("Fetching list of topic type URIs failed", e); 306 } 307 } 308 309 @Override 310 public TopicType getTopicType(String uri) { 311 try { 312 return typeCache.getTopicType(uri); 313 } catch (Exception e) { 314 throw new RuntimeException("Fetching topic type \"" + uri + "\" failed", e); 315 } 316 } 317 318 @Override 319 public List<TopicType> getAllTopicTypes() { 320 try { 321 List<TopicType> topicTypes = new ArrayList(); 322 for (String uri : getTopicTypeUris()) { 323 TopicType topicType = getTopicType(uri); 324 topicTypes.add(topicType); 325 } 326 return topicTypes; 327 } catch (Exception e) { 328 throw new RuntimeException("Fetching all topic types failed", e); 329 } 330 } 331 332 // --- 333 334 @Override 335 public TopicType createTopicType(TopicTypeModel model) { 336 try { 337 TopicType topicType = topicTypeFactory(model); 338 fireEvent(CoreEvent.INTRODUCE_TOPIC_TYPE, topicType); 339 return topicType; 340 } catch (Exception e) { 341 throw new RuntimeException("Creating topic type \"" + model.getUri() + "\" failed (" + model + ")", e); 342 } 343 } 344 345 @Override 346 public void updateTopicType(TopicTypeModel model) { 347 try { 348 // Note: type lookup is by ID. The URI might have changed, the ID does not. 349 String topicTypeUri = getTopic(model.getId()).getUri(); 350 getTopicType(topicTypeUri).update(model); 351 } catch (Exception e) { 352 throw new RuntimeException("Updating topic type failed (" + model + ")", e); 353 } 354 } 355 356 @Override 357 public void deleteTopicType(String topicTypeUri) { 358 try { 359 getTopicType(topicTypeUri).delete(); 360 } catch (Exception e) { 361 throw new RuntimeException("Deleting topic type \"" + topicTypeUri + "\" failed", e); 362 } 363 } 364 365 366 367 // === Association Types === 368 369 @Override 370 public List<String> getAssociationTypeUris() { 371 try { 372 Topic metaType = instantiateTopic(storageDecorator.fetchTopic("uri", 373 new SimpleValue("dm4.core.assoc_type"))); 374 ResultList<RelatedTopic> assocTypes = metaType.getRelatedTopics("dm4.core.instantiation", "dm4.core.type", 375 "dm4.core.instance", "dm4.core.assoc_type", 0); 376 List<String> assocTypeUris = new ArrayList(); 377 for (Topic assocType : assocTypes) { 378 assocTypeUris.add(assocType.getUri()); 379 } 380 return assocTypeUris; 381 } catch (Exception e) { 382 throw new RuntimeException("Fetching list of association type URIs failed", e); 383 } 384 } 385 386 @Override 387 public AssociationType getAssociationType(String uri) { 388 try { 389 return typeCache.getAssociationType(uri); 390 } catch (Exception e) { 391 throw new RuntimeException("Fetching association type \"" + uri + "\" failed", e); 392 } 393 } 394 395 @Override 396 public List<AssociationType> getAllAssociationTypes() { 397 try { 398 List<AssociationType> assocTypes = new ArrayList(); 399 for (String uri : getAssociationTypeUris()) { 400 AssociationType assocType = getAssociationType(uri); 401 assocTypes.add(assocType); 402 } 403 return assocTypes; 404 } catch (Exception e) { 405 throw new RuntimeException("Fetching all association types failed", e); 406 } 407 } 408 409 // --- 410 411 @Override 412 public AssociationType createAssociationType(AssociationTypeModel model) { 413 try { 414 AssociationType assocType = associationTypeFactory(model); 415 fireEvent(CoreEvent.INTRODUCE_ASSOCIATION_TYPE, assocType); 416 return assocType; 417 } catch (Exception e) { 418 throw new RuntimeException("Creating association type \"" + model.getUri() + "\" failed (" + model + ")", 419 e); 420 } 421 } 422 423 @Override 424 public void updateAssociationType(AssociationTypeModel model) { 425 try { 426 // Note: type lookup is by ID. The URI might have changed, the ID does not. 427 String assocTypeUri = getTopic(model.getId()).getUri(); 428 getAssociationType(assocTypeUri).update(model); 429 } catch (Exception e) { 430 throw new RuntimeException("Updating association type failed (" + model + ")", e); 431 } 432 } 433 434 @Override 435 public void deleteAssociationType(String assocTypeUri) { 436 try { 437 getAssociationType(assocTypeUri).delete(); 438 } catch (Exception e) { 439 throw new RuntimeException("Deleting association type \"" + assocTypeUri + "\" failed", e); 440 } 441 } 442 443 444 445 // === Role Types === 446 447 @Override 448 public Topic createRoleType(TopicModel model) { 449 // check type URI argument 450 String typeUri = model.getTypeUri(); 451 if (typeUri == null) { 452 model.setTypeUri("dm4.core.role_type"); 453 } else { 454 if (!typeUri.equals("dm4.core.role_type")) { 455 throw new IllegalArgumentException("A role type is supposed to be of type \"dm4.core.role_type\" " + 456 "(found: \"" + typeUri + "\")"); 457 } 458 } 459 // 460 return createTopic(model, URI_PREFIX_ROLE_TYPE); 461 } 462 463 464 465 // === Plugins === 466 467 @Override 468 public Plugin getPlugin(String pluginUri) { 469 return pluginManager.getPlugin(pluginUri); 470 } 471 472 @Override 473 public List<PluginInfo> getPluginInfo() { 474 return pluginManager.getPluginInfo(); 475 } 476 477 478 479 // === Events === 480 481 @Override 482 public void fireEvent(DeepaMehtaEvent event, Object... params) { 483 eventManager.fireEvent(event, params); 484 } 485 486 @Override 487 public void deliverEvent(String pluginUri, DeepaMehtaEvent event, Object... params) { 488 eventManager.deliverEvent(pluginUri, event, params); 489 } 490 491 492 493 // === Properties === 494 495 @Override 496 public List<Topic> getTopicsByProperty(String propUri, Object propValue) { 497 return instantiateTopics(storageDecorator.fetchTopicsByProperty(propUri, propValue)); 498 } 499 500 @Override 501 public List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) { 502 return instantiateTopics(storageDecorator.fetchTopicsByPropertyRange(propUri, from, to)); 503 } 504 505 @Override 506 public List<Association> getAssociationsByProperty(String propUri, Object propValue) { 507 return instantiateAssociations(storageDecorator.fetchAssociationsByProperty(propUri, propValue)); 508 } 509 510 @Override 511 public List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) { 512 return instantiateAssociations(storageDecorator.fetchAssociationsByPropertyRange(propUri, from, to)); 513 } 514 515 516 517 // === Misc === 518 519 @Override 520 public DeepaMehtaTransaction beginTx() { 521 return storageDecorator.beginTx(); 522 } 523 524 @Override 525 public TypeStorage getTypeStorage() { 526 return typeStorage; 527 } 528 529 @Override 530 public Object getDatabaseVendorObject() { 531 return storageDecorator.getDatabaseVendorObject(); 532 } 533 534 535 536 // ----------------------------------------------------------------------------------------- Package Private Methods 537 538 539 540 // === Helper === 541 542 void createTopicInstantiation(long topicId, String topicTypeUri) { 543 try { 544 AssociationModel assoc = new AssociationModel("dm4.core.instantiation", 545 new TopicRoleModel(topicTypeUri, "dm4.core.type"), 546 new TopicRoleModel(topicId, "dm4.core.instance")); 547 storageDecorator.storeAssociation(assoc); // direct storage calls used here ### explain 548 storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 549 createAssociationInstantiation(assoc.getId(), assoc.getTypeUri()); 550 } catch (Exception e) { 551 throw new RuntimeException("Associating topic " + topicId + 552 " with topic type \"" + topicTypeUri + "\" failed", e); 553 } 554 } 555 556 void createAssociationInstantiation(long assocId, String assocTypeUri) { 557 try { 558 AssociationModel assoc = new AssociationModel("dm4.core.instantiation", 559 new TopicRoleModel(assocTypeUri, "dm4.core.type"), 560 new AssociationRoleModel(assocId, "dm4.core.instance")); 561 storageDecorator.storeAssociation(assoc); // direct storage calls used here ### explain 562 storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 563 } catch (Exception e) { 564 throw new RuntimeException("Associating association " + assocId + 565 " with association type \"" + assocTypeUri + "\" failed", e); 566 } 567 } 568 569 // --- 570 571 /** 572 * Convenience method. ### to be dropped? 573 */ 574 Association createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 575 return createAssociation(new AssociationModel(typeUri, roleModel1, roleModel2)); 576 } 577 578 // ------------------------------------------------------------------------------------------------- Private Methods 579 580 581 582 // === Instantiation === 583 584 /** 585 * Attaches this core service to a topic model fetched from storage layer. 586 */ 587 Topic instantiateTopic(TopicModel model) { 588 return new AttachedTopic(model, this); 589 } 590 591 private List<Topic> instantiateTopics(List<TopicModel> models) { 592 List<Topic> topics = new ArrayList(); 593 for (TopicModel model : models) { 594 topics.add(instantiateTopic(model)); 595 } 596 return topics; 597 } 598 599 // --- 600 601 RelatedTopic instantiateRelatedTopic(RelatedTopicModel model) { 602 return new AttachedRelatedTopic(model, this); 603 } 604 605 ResultList<RelatedTopic> instantiateRelatedTopics(ResultList<RelatedTopicModel> models) { 606 List<RelatedTopic> relTopics = new ArrayList(); 607 for (RelatedTopicModel model : models) { 608 relTopics.add(instantiateRelatedTopic(model)); 609 } 610 return new ResultList<RelatedTopic>(models.getTotalCount(), relTopics); 611 } 612 613 // === 614 615 /** 616 * Attaches this core service to an association fetched from storage layer. 617 */ 618 Association instantiateAssociation(AssociationModel model) { 619 return new AttachedAssociation(model, this); 620 } 621 622 List<Association> instantiateAssociations(List<AssociationModel> models) { 623 List<Association> assocs = new ArrayList(); 624 for (AssociationModel model : models) { 625 assocs.add(instantiateAssociation(model)); 626 } 627 return assocs; 628 } 629 630 // --- 631 632 RelatedAssociation instantiateRelatedAssociation(RelatedAssociationModel model) { 633 return new AttachedRelatedAssociation(model, this); 634 } 635 636 ResultList<RelatedAssociation> instantiateRelatedAssociations(Iterable<RelatedAssociationModel> models) { 637 ResultList<RelatedAssociation> relAssocs = new ResultList(); 638 for (RelatedAssociationModel model : models) { 639 relAssocs.add(instantiateRelatedAssociation(model)); 640 } 641 return relAssocs; 642 } 643 644 645 646 // === Factory === 647 648 private Topic createTopic(TopicModel model, String uriPrefix) { 649 try { 650 fireEvent(CoreEvent.PRE_CREATE_TOPIC, model); 651 Topic topic = topicFactory(model, uriPrefix); 652 fireEvent(CoreEvent.POST_CREATE_TOPIC, topic); 653 return topic; 654 } catch (Exception e) { 655 throw new RuntimeException("Creating topic failed (" + model + ")", e); 656 } 657 } 658 659 // --- 660 661 /** 662 * Factory method: creates a new topic in the DB according to the given topic model and returns a topic instance. 663 */ 664 private Topic topicFactory(TopicModel model, String uriPrefix) { 665 // 1) store in DB 666 storageDecorator.storeTopic(model); 667 valueStorage.storeValue(model); 668 createTopicInstantiation(model.getId(), model.getTypeUri()); 669 // 670 // 2) instantiate 671 Topic topic = new AttachedTopic(model, this); 672 // 673 // 3) set default URI 674 // If no URI is given the topic gets a default URI based on its ID, if requested. 675 // Note: this must be done *after* the topic is stored. The ID is not known before. 676 // Note: in case no URI was given: once stored a topic's URI is empty (not null). 677 if (uriPrefix != null && topic.getUri().equals("")) { 678 topic.setUri(uriPrefix + topic.getId()); 679 } 680 // 681 return topic; 682 } 683 684 /** 685 * Factory method: creates a new association in the DB according to the given association model and returns an 686 * association instance. 687 */ 688 private Association associationFactory(AssociationModel model) { 689 // 1) store in DB 690 storageDecorator.storeAssociation(model); 691 valueStorage.storeValue(model); 692 createAssociationInstantiation(model.getId(), model.getTypeUri()); 693 // 694 // 2) instantiate 695 return new AttachedAssociation(model, this); 696 } 697 698 // --- 699 700 /** 701 * Factory method: creates a new topic type in the DB according to the given topic type model 702 * and returns a topic type instance. 703 */ 704 private TopicType topicTypeFactory(TopicTypeModel model) { 705 // 1) store in DB 706 topicFactory(model, URI_PREFIX_TOPIC_TYPE); // store generic topic 707 typeStorage.storeType(model); // store type-specific parts 708 // 709 // 2) instantiate 710 TopicType topicType = new AttachedTopicType(model, this); 711 typeCache.putTopicType(topicType); 712 // 713 return topicType; 714 } 715 716 /** 717 * Factory method: creates a new association type in the DB according to the given association type model 718 * and returns a topic type instance. 719 */ 720 private AssociationType associationTypeFactory(AssociationTypeModel model) { 721 // 1) store in DB 722 topicFactory(model, URI_PREFIX_ASSOCIATION_TYPE); // store generic topic 723 typeStorage.storeType(model); // store type-specific parts 724 // 725 // 2) instantiate 726 AssociationType assocType = new AttachedAssociationType(model, this); 727 typeCache.putAssociationType(assocType); 728 // 729 return assocType; 730 } 731 732 733 734 // === Bootstrap === 735 736 /** 737 * Setups the database: 738 * 1) initializes the database. 739 * 2) in case of a clean install: sets up the bootstrap content. 740 * 3) runs the core migrations. 741 */ 742 private void setupDB() { 743 DeepaMehtaTransaction tx = beginTx(); 744 try { 745 logger.info("----- Setting up the database -----"); 746 boolean isCleanInstall = storageDecorator.init(); 747 if (isCleanInstall) { 748 setupBootstrapContent(); 749 } 750 migrationManager.runCoreMigrations(isCleanInstall); 751 tx.success(); 752 tx.finish(); 753 logger.info("----- Setting up the database complete -----"); 754 } catch (Exception e) { 755 logger.warning("ROLLBACK!"); 756 // Note: we don't put finish() in a finally clause here because 757 // in case of error the database has to be shut down. 758 tx.finish(); 759 storageDecorator.shutdown(); 760 throw new RuntimeException("Setting up the database failed", e); 761 } 762 } 763 764 private void setupBootstrapContent() { 765 try { 766 // Create meta types "Topic Type" and "Association Type" -- needed to create topic types and 767 // asscociation types 768 TopicModel t = new TopicModel("dm4.core.topic_type", "dm4.core.meta_type", 769 new SimpleValue("Topic Type")); 770 TopicModel a = new TopicModel("dm4.core.assoc_type", "dm4.core.meta_type", 771 new SimpleValue("Association Type")); 772 _createTopic(t); 773 _createTopic(a); 774 // Create topic types "Data Type" and "Role Type" 775 // ### Note: the topic type "Data Type" depends on the data type "Text" and the data type "Text" in turn 776 // depends on the topic type "Data Type". To resolve this circle we use a low-level (storage) call here 777 // and postpone the data type association. 778 TopicModel dataType = new TopicTypeModel("dm4.core.data_type", "Data Type", "dm4.core.text"); 779 TopicModel roleType = new TopicTypeModel("dm4.core.role_type", "Role Type", "dm4.core.text"); 780 _createTopic(dataType); 781 _createTopic(roleType); 782 // Create data type "Text" 783 TopicModel text = new TopicModel("dm4.core.text", "dm4.core.data_type", new SimpleValue("Text")); 784 _createTopic(text); 785 // Create role types "Default", "Type", and "Instance" 786 TopicModel deflt = new TopicModel("dm4.core.default", "dm4.core.role_type", new SimpleValue("Default")); 787 TopicModel type = new TopicModel("dm4.core.type", "dm4.core.role_type", new SimpleValue("Type")); 788 TopicModel inst = new TopicModel("dm4.core.instance", "dm4.core.role_type", new SimpleValue("Instance")); 789 _createTopic(deflt); 790 _createTopic(type); 791 _createTopic(inst); 792 // Create association type "Aggregation" -- needed to associate topic/association types with data types 793 TopicModel aggregation = new AssociationTypeModel("dm4.core.aggregation", "Aggregation", "dm4.core.text"); 794 _createTopic(aggregation); 795 // Create association type "Instantiation" -- needed to associate topics with topic types 796 TopicModel instn = new AssociationTypeModel("dm4.core.instantiation", "Instantiation", "dm4.core.text"); 797 _createTopic(instn); 798 // 799 // 1) Postponed topic type association 800 // 801 // Note: createTopicInstantiation() creates the associations by *low-level* (storage) calls. 802 // That's why the associations can be created *before* their type (here: "dm4.core.instantiation") 803 // is fully constructed (the type's data type is not yet associated => step 2). 804 createTopicInstantiation(t.getId(), t.getTypeUri()); 805 createTopicInstantiation(a.getId(), a.getTypeUri()); 806 createTopicInstantiation(dataType.getId(), dataType.getTypeUri()); 807 createTopicInstantiation(roleType.getId(), roleType.getTypeUri()); 808 createTopicInstantiation(text.getId(), text.getTypeUri()); 809 createTopicInstantiation(deflt.getId(), deflt.getTypeUri()); 810 createTopicInstantiation(type.getId(), type.getTypeUri()); 811 createTopicInstantiation(inst.getId(), inst.getTypeUri()); 812 createTopicInstantiation(aggregation.getId(), aggregation.getTypeUri()); 813 createTopicInstantiation(instn.getId(), instn.getTypeUri()); 814 // 815 // 2) Postponed data type association 816 // 817 // Note: associateDataType() creates the association by a *high-level* (service) call. 818 // This requires the association type (here: dm4.core.aggregation) to be fully constructed already. 819 // That's why the topic type associations (step 1) must be performed *before* the data type associations. 820 // ### FIXDOC: not true anymore 821 // 822 // Note: at time of the first associateDataType() call the required association type (dm4.core.aggregation) 823 // is *not* fully constructed yet! (it gets constructed through this very call). This works anyway because 824 // the data type assigning association is created *before* the association type is fetched. 825 // (see AttachedAssociation.store(): storage.storeAssociation() is called before getType() 826 // in AttachedDeepaMehtaObject.store().) 827 // ### FIXDOC: not true anymore 828 // 829 // Important is that associateDataType("dm4.core.aggregation") is the first call here. 830 // ### FIXDOC: not true anymore 831 // 832 // Note: _associateDataType() creates the data type assigning association by a *low-level* (storage) call. 833 // A high-level (service) call would fail while setting the association's value. The involved getType() 834 // would fail (not because the association is missed -- it's created meanwhile, but) 835 // because this involves fetching the association including its value. The value doesn't exist yet, 836 // because its setting forms the begin of this vicious circle. 837 _associateDataType("dm4.core.meta_type", "dm4.core.text"); 838 _associateDataType("dm4.core.topic_type", "dm4.core.text"); 839 _associateDataType("dm4.core.assoc_type", "dm4.core.text"); 840 _associateDataType("dm4.core.data_type", "dm4.core.text"); 841 _associateDataType("dm4.core.role_type", "dm4.core.text"); 842 // 843 _associateDataType("dm4.core.aggregation", "dm4.core.text"); 844 _associateDataType("dm4.core.instantiation", "dm4.core.text"); 845 } catch (Exception e) { 846 throw new RuntimeException("Setting up the bootstrap content failed", e); 847 } 848 } 849 850 // --- 851 852 /** 853 * Low-level method that stores a topic without its "Instantiation" association. 854 * Needed for bootstrapping. 855 */ 856 private void _createTopic(TopicModel model) { 857 storageDecorator.storeTopic(model); 858 storageDecorator.storeTopicValue(model.getId(), model.getSimpleValue()); 859 } 860 861 /** 862 * Low-level method that stores an (data type) association without its "Instantiation" association. 863 * Needed for bootstrapping. 864 */ 865 private void _associateDataType(String typeUri, String dataTypeUri) { 866 AssociationModel assoc = new AssociationModel("dm4.core.aggregation", 867 new TopicRoleModel(typeUri, "dm4.core.type"), 868 new TopicRoleModel(dataTypeUri, "dm4.core.default")); 869 storageDecorator.storeAssociation(assoc); 870 storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 871 } 872 873 // --- 874 875 private void bootstrapTypeCache() { 876 typeCache.putTopicType(new AttachedTopicType(new TopicTypeModel("dm4.core.meta_meta_type", 877 "dm4.core.meta_meta_meta_type", "Meta Meta Type", "dm4.core.text"), this)); 878 } 879 }