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