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