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