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