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