001package de.deepamehta.core.impl; 002 003import de.deepamehta.core.Association; 004import de.deepamehta.core.AssociationType; 005import de.deepamehta.core.DeepaMehtaObject; 006import de.deepamehta.core.RelatedTopic; 007import de.deepamehta.core.Topic; 008import de.deepamehta.core.TopicType; 009import de.deepamehta.core.model.AssociationModel; 010import de.deepamehta.core.model.AssociationTypeModel; 011import de.deepamehta.core.model.RoleModel; 012import de.deepamehta.core.model.SimpleValue; 013import de.deepamehta.core.model.TopicModel; 014import de.deepamehta.core.model.TopicTypeModel; 015import de.deepamehta.core.service.CoreService; 016import de.deepamehta.core.service.DeepaMehtaEvent; 017import de.deepamehta.core.service.ModelFactory; 018import de.deepamehta.core.service.Plugin; 019import de.deepamehta.core.service.PluginInfo; 020import de.deepamehta.core.service.accesscontrol.AccessControl; 021import de.deepamehta.core.storage.spi.DeepaMehtaTransaction; 022 023import org.osgi.framework.BundleContext; 024 025import java.util.ArrayList; 026import java.util.List; 027import java.util.logging.Logger; 028 029 030 031/** 032 * Implementation of the DeepaMehta core service. Embeddable into Java applications. 033 */ 034public class CoreServiceImpl implements CoreService { 035 036 // ---------------------------------------------------------------------------------------------- Instance Variables 037 038 BundleContext bundleContext; 039 PersistenceLayer pl; 040 EventManager em; 041 ModelFactory mf; 042 MigrationManager migrationManager; 043 PluginManager pluginManager; 044 AccessControl accessControl; 045 WebPublishingService wpService; 046 047 private Logger logger = Logger.getLogger(getClass().getName()); 048 049 // ---------------------------------------------------------------------------------------------------- Constructors 050 051 /** 052 * @param bundleContext The context of the DeepaMehta 4 Core bundle. 053 */ 054 public CoreServiceImpl(PersistenceLayer pl, BundleContext bundleContext) { 055 this.bundleContext = bundleContext; 056 this.pl = pl; 057 this.em = pl.em; 058 this.mf = pl.mf; 059 this.migrationManager = new MigrationManager(this); 060 this.pluginManager = new PluginManager(this); 061 this.accessControl = new AccessControlImpl(pl); 062 this.wpService = new WebPublishingService(pl); 063 // 064 setupDB(); 065 } 066 067 // -------------------------------------------------------------------------------------------------- Public Methods 068 069 070 071 // ********************************** 072 // *** CoreService Implementation *** 073 // ********************************** 074 075 076 077 // === Topics === 078 079 @Override 080 public Topic getTopic(long topicId) { 081 return pl.getTopic(topicId); 082 } 083 084 @Override 085 public TopicImpl getTopicByUri(String uri) { 086 return pl.getTopicByUri(uri); 087 } 088 089 @Override 090 public Topic getTopicByValue(String key, SimpleValue value) { 091 return pl.getTopicByValue(key, value); 092 } 093 094 @Override 095 public List<Topic> getTopicsByValue(String key, SimpleValue value) { 096 return pl.getTopicsByValue(key, value); 097 } 098 099 @Override 100 public List<Topic> getTopicsByType(String topicTypeUri) { 101 return pl.getTopicsByType(topicTypeUri); 102 } 103 104 @Override 105 public List<Topic> searchTopics(String searchTerm, String fieldUri) { 106 return pl.searchTopics(searchTerm, fieldUri); 107 } 108 109 @Override 110 public Iterable<Topic> getAllTopics() { 111 return pl.getAllTopics(); 112 } 113 114 // --- 115 116 @Override 117 public TopicImpl createTopic(TopicModel model) { 118 return pl.createTopic((TopicModelImpl) model); 119 } 120 121 @Override 122 public void updateTopic(TopicModel updateModel) { 123 pl.updateTopic((TopicModelImpl) updateModel); 124 } 125 126 @Override 127 public void deleteTopic(long topicId) { 128 pl.deleteTopic(topicId); 129 } 130 131 132 133 // === Associations === 134 135 @Override 136 public Association getAssociation(long assocId) { 137 return pl.getAssociation(assocId); 138 } 139 140 @Override 141 public Association getAssociationByValue(String key, SimpleValue value) { 142 return pl.getAssociationByValue(key, value); 143 } 144 145 @Override 146 public List<Association> getAssociationsByValue(String key, SimpleValue value) { 147 return pl.getAssociationsByValue(key, value); 148 } 149 150 @Override 151 public Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id, 152 String roleTypeUri1, String roleTypeUri2) { 153 return pl.getAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2); 154 } 155 156 @Override 157 public Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId, 158 String topicRoleTypeUri, String assocRoleTypeUri) { 159 return pl.getAssociationBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, topicRoleTypeUri, 160 assocRoleTypeUri); 161 } 162 163 // --- 164 165 @Override 166 public List<Association> getAssociationsByType(String assocTypeUri) { 167 return pl.getAssociationsByType(assocTypeUri); 168 } 169 170 @Override 171 public List<Association> getAssociations(long topic1Id, long topic2Id) { 172 return pl.getAssociations(topic1Id, topic2Id); 173 } 174 175 @Override 176 public List<Association> getAssociations(long topic1Id, long topic2Id, String assocTypeUri) { 177 return pl.getAssociations(assocTypeUri, topic1Id, topic2Id); 178 } 179 180 // --- 181 182 @Override 183 public Iterable<Association> getAllAssociations() { 184 return pl.getAllAssociations(); 185 } 186 187 @Override 188 public long[] getPlayerIds(long assocId) { 189 return pl.getPlayerIds(assocId); 190 } 191 192 // --- 193 194 @Override 195 public AssociationImpl createAssociation(AssociationModel model) { 196 return pl.createAssociation((AssociationModelImpl) model); 197 } 198 199 @Override 200 public void updateAssociation(AssociationModel updateModel) { 201 pl.updateAssociation((AssociationModelImpl) updateModel); 202 } 203 204 @Override 205 public void deleteAssociation(long assocId) { 206 pl.deleteAssociation(assocId); 207 } 208 209 210 211 // === Topic Types === 212 213 @Override 214 public TopicTypeImpl getTopicType(String uri) { 215 return pl.getTopicType(uri); 216 } 217 218 @Override 219 public TopicTypeImpl getTopicTypeImplicitly(long topicId) { 220 return pl.getTopicTypeImplicitly(topicId); 221 } 222 223 // --- 224 225 @Override 226 public List<TopicType> getAllTopicTypes() { 227 return pl.getAllTopicTypes(); 228 } 229 230 // --- 231 232 @Override 233 public TopicTypeImpl createTopicType(TopicTypeModel model) { 234 return pl.createTopicType((TopicTypeModelImpl) model); 235 } 236 237 @Override 238 public void updateTopicType(TopicTypeModel updateModel) { 239 pl.updateTopicType((TopicTypeModelImpl) updateModel); 240 } 241 242 @Override 243 public void deleteTopicType(String topicTypeUri) { 244 pl.deleteTopicType(topicTypeUri); 245 } 246 247 248 249 // === Association Types === 250 251 @Override 252 public AssociationTypeImpl getAssociationType(String uri) { 253 return pl.getAssociationType(uri); 254 } 255 256 @Override 257 public AssociationTypeImpl getAssociationTypeImplicitly(long assocId) { 258 return pl.getAssociationTypeImplicitly(assocId); 259 } 260 261 // --- 262 263 @Override 264 public List<AssociationType> getAllAssociationTypes() { 265 return pl.getAllAssociationTypes(); 266 } 267 268 // --- 269 270 @Override 271 public AssociationTypeImpl createAssociationType(AssociationTypeModel model) { 272 return pl.createAssociationType((AssociationTypeModelImpl) model); 273 } 274 275 @Override 276 public void updateAssociationType(AssociationTypeModel updateModel) { 277 pl.updateAssociationType((AssociationTypeModelImpl) updateModel); 278 } 279 280 @Override 281 public void deleteAssociationType(String assocTypeUri) { 282 pl.deleteAssociationType(assocTypeUri); 283 } 284 285 286 287 // === Role Types === 288 289 @Override 290 public Topic createRoleType(TopicModel model) { 291 return pl.createRoleType((TopicModelImpl) model); 292 } 293 294 295 296 // === Generic Object === 297 298 @Override 299 public DeepaMehtaObject getObject(long id) { 300 return pl.getObject(id); 301 } 302 303 304 305 // === Plugins === 306 307 @Override 308 public PluginImpl getPlugin(String pluginUri) { 309 return pluginManager.getPlugin(pluginUri); 310 } 311 312 @Override 313 public List<PluginInfo> getPluginInfo() { 314 return pluginManager.getPluginInfo(); 315 } 316 317 318 319 // === Events === 320 321 @Override 322 public void fireEvent(DeepaMehtaEvent event, Object... params) { 323 em.fireEvent(event, params); 324 } 325 326 @Override 327 public void dispatchEvent(String pluginUri, DeepaMehtaEvent event, Object... params) { 328 em.dispatchEvent(getPlugin(pluginUri), event, params); 329 } 330 331 332 333 // === Properties === 334 335 @Override 336 public Object getProperty(long id, String propUri) { 337 return pl.fetchProperty(id, propUri); 338 } 339 340 @Override 341 public boolean hasProperty(long id, String propUri) { 342 return pl.hasProperty(id, propUri); 343 } 344 345 // --- 346 347 @Override 348 public List<Topic> getTopicsByProperty(String propUri, Object propValue) { 349 return pl.getTopicsByProperty(propUri, propValue); 350 } 351 352 @Override 353 public List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) { 354 return pl.getTopicsByPropertyRange(propUri, from, to); 355 } 356 357 @Override 358 public List<Association> getAssociationsByProperty(String propUri, Object propValue) { 359 return pl.getAssociationsByProperty(propUri, propValue); 360 } 361 362 @Override 363 public List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) { 364 return pl.getAssociationsByPropertyRange(propUri, from, to); 365 } 366 367 // --- 368 369 @Override 370 public void addTopicPropertyIndex(String propUri) { 371 int topics = 0; 372 int added = 0; 373 logger.info("########## Adding topic property index for \"" + propUri + "\""); 374 for (Topic topic : getAllTopics()) { 375 if (topic.hasProperty(propUri)) { 376 Object value = topic.getProperty(propUri); 377 pl.indexTopicProperty(topic.getId(), propUri, value); 378 added++; 379 } 380 topics++; 381 } 382 logger.info("########## Adding topic property index complete\n Topics processed: " + topics + 383 "\n added to index: " + added); 384 } 385 386 @Override 387 public void addAssociationPropertyIndex(String propUri) { 388 int assocs = 0; 389 int added = 0; 390 logger.info("########## Adding association property index for \"" + propUri + "\""); 391 for (Association assoc : getAllAssociations()) { 392 if (assoc.hasProperty(propUri)) { 393 Object value = assoc.getProperty(propUri); 394 pl.indexAssociationProperty(assoc.getId(), propUri, value); 395 added++; 396 } 397 assocs++; 398 } 399 logger.info("########## Adding association property complete\n Associations processed: " + assocs + 400 "\n added to index: " + added); 401 } 402 403 404 405 // === Misc === 406 407 @Override 408 public DeepaMehtaTransaction beginTx() { 409 return pl.beginTx(); 410 } 411 412 // --- 413 414 @Override 415 public ModelFactory getModelFactory() { 416 return mf; 417 } 418 419 @Override 420 public AccessControl getAccessControl() { 421 return accessControl; 422 } 423 424 @Override 425 public Object getDatabaseVendorObject() { 426 return pl.getDatabaseVendorObject(); 427 } 428 429 // ----------------------------------------------------------------------------------------- Package Private Methods 430 431 432 433 // === Helper === 434 435 /** 436 * Convenience method. 437 */ 438 Association createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 439 return createAssociation(mf.newAssociationModel(typeUri, roleModel1, roleModel2)); 440 } 441 442 // ------------------------------------------------------------------------------------------------- Private Methods 443 444 445 446 // === Bootstrap === 447 448 /** 449 * Setups the database: 450 * 1) initializes the database. 451 * 2) in case of a clean install: sets up the bootstrap content. 452 * 3) runs the core migrations. 453 */ 454 private void setupDB() { 455 DeepaMehtaTransaction tx = beginTx(); 456 try { 457 logger.info("----- Setting up the database -----"); 458 boolean isCleanInstall = pl.init(); 459 if (isCleanInstall) { 460 setupBootstrapContent(); 461 } 462 migrationManager.runCoreMigrations(isCleanInstall); 463 tx.success(); 464 tx.finish(); 465 logger.info("----- Setting up the database complete -----"); 466 } catch (Exception e) { 467 logger.warning("ROLLBACK!"); 468 // Note: we don't put finish() in a finally clause here because 469 // in case of error the database has to be shut down. 470 tx.finish(); 471 pl.shutdown(); 472 throw new RuntimeException("Setting up the database failed", e); 473 } 474 } 475 476 private void setupBootstrapContent() { 477 try { 478 // Create meta types "Topic Type" and "Association Type" -- needed to create topic types and 479 // asscociation types 480 TopicModel t = mf.newTopicModel("dm4.core.topic_type", "dm4.core.meta_type", 481 new SimpleValue("Topic Type")); 482 TopicModel a = mf.newTopicModel("dm4.core.assoc_type", "dm4.core.meta_type", 483 new SimpleValue("Association Type")); 484 _createTopic(t); 485 _createTopic(a); 486 // Create topic types "Data Type" and "Role Type" 487 // ### Note: the topic type "Data Type" depends on the data type "Text" and the data type "Text" in turn 488 // depends on the topic type "Data Type". To resolve this circle we use a low-level (storage) call here 489 // and postpone the data type association. 490 TopicModel dataType = mf.newTopicTypeModel("dm4.core.data_type", "Data Type", "dm4.core.text"); 491 TopicModel roleType = mf.newTopicTypeModel("dm4.core.role_type", "Role Type", "dm4.core.text"); 492 _createTopic(dataType); 493 _createTopic(roleType); 494 // Create data type "Text" 495 TopicModel text = mf.newTopicModel("dm4.core.text", "dm4.core.data_type", new SimpleValue("Text")); 496 _createTopic(text); 497 // Create role types "Default", "Type", and "Instance" 498 TopicModel deflt = mf.newTopicModel("dm4.core.default", "dm4.core.role_type", new SimpleValue("Default")); 499 TopicModel type = mf.newTopicModel("dm4.core.type", "dm4.core.role_type", new SimpleValue("Type")); 500 TopicModel inst = mf.newTopicModel("dm4.core.instance", "dm4.core.role_type", new SimpleValue("Instance")); 501 _createTopic(deflt); 502 _createTopic(type); 503 _createTopic(inst); 504 // Create association type "Aggregation" -- needed to associate topic/association types with data types 505 TopicModel aggregation = mf.newAssociationTypeModel("dm4.core.aggregation", "Aggregation", "dm4.core.text"); 506 _createTopic(aggregation); 507 // Create association type "Instantiation" -- needed to associate topics with topic types 508 TopicModel instn = mf.newAssociationTypeModel("dm4.core.instantiation", "Instantiation", "dm4.core.text"); 509 _createTopic(instn); 510 // 511 // 1) Postponed topic type association 512 // 513 // Note: createTopicInstantiation() creates the associations by *low-level* (storage) calls. 514 // That's why the associations can be created *before* their type (here: "dm4.core.instantiation") 515 // is fully constructed (the type's data type is not yet associated => step 2). 516 pl.createTopicInstantiation(t.getId(), t.getTypeUri()); 517 pl.createTopicInstantiation(a.getId(), a.getTypeUri()); 518 pl.createTopicInstantiation(dataType.getId(), dataType.getTypeUri()); 519 pl.createTopicInstantiation(roleType.getId(), roleType.getTypeUri()); 520 pl.createTopicInstantiation(text.getId(), text.getTypeUri()); 521 pl.createTopicInstantiation(deflt.getId(), deflt.getTypeUri()); 522 pl.createTopicInstantiation(type.getId(), type.getTypeUri()); 523 pl.createTopicInstantiation(inst.getId(), inst.getTypeUri()); 524 pl.createTopicInstantiation(aggregation.getId(), aggregation.getTypeUri()); 525 pl.createTopicInstantiation(instn.getId(), instn.getTypeUri()); 526 // 527 // 2) Postponed data type association 528 // 529 // Note: associateDataType() creates the association by a *high-level* (service) call. 530 // This requires the association type (here: dm4.core.aggregation) to be fully constructed already. 531 // That's why the topic type associations (step 1) must be performed *before* the data type associations. 532 // ### FIXDOC: not true anymore 533 // 534 // Note: at time of the first associateDataType() call the required association type (dm4.core.aggregation) 535 // is *not* fully constructed yet! (it gets constructed through this very call). This works anyway because 536 // the data type assigning association is created *before* the association type is fetched. 537 // (see AssociationImpl.store(): storage.storeAssociation() is called before getType() 538 // in DeepaMehtaObjectImpl.store().) 539 // ### FIXDOC: not true anymore 540 // 541 // Important is that associateDataType("dm4.core.aggregation") is the first call here. 542 // ### FIXDOC: not true anymore 543 // 544 // Note: _associateDataType() creates the data type assigning association by a *low-level* (storage) call. 545 // A high-level (service) call would fail while setting the association's value. The involved getType() 546 // would fail (not because the association is missed -- it's created meanwhile, but) 547 // because this involves fetching the association including its value. The value doesn't exist yet, 548 // because its setting forms the begin of this vicious circle. 549 _associateDataType("dm4.core.meta_type", "dm4.core.text"); 550 _associateDataType("dm4.core.topic_type", "dm4.core.text"); 551 _associateDataType("dm4.core.assoc_type", "dm4.core.text"); 552 _associateDataType("dm4.core.data_type", "dm4.core.text"); 553 _associateDataType("dm4.core.role_type", "dm4.core.text"); 554 // 555 _associateDataType("dm4.core.aggregation", "dm4.core.text"); 556 _associateDataType("dm4.core.instantiation", "dm4.core.text"); 557 } catch (Exception e) { 558 throw new RuntimeException("Setting up the bootstrap content failed", e); 559 } 560 } 561 562 // --- 563 564 /** 565 * Low-level method that stores a topic without its "Instantiation" association. 566 * Needed for bootstrapping. 567 */ 568 private void _createTopic(TopicModel model) { 569 pl.storeTopic(model); 570 pl.storeTopicValue(model.getId(), model.getSimpleValue()); 571 } 572 573 /** 574 * Low-level method that stores an (data type) association without its "Instantiation" association. 575 * Needed for bootstrapping. 576 */ 577 private void _associateDataType(String typeUri, String dataTypeUri) { 578 AssociationModel assoc = mf.newAssociationModel("dm4.core.aggregation", 579 mf.newTopicRoleModel(typeUri, "dm4.core.type"), 580 mf.newTopicRoleModel(dataTypeUri, "dm4.core.default")); 581 pl.storeAssociation(assoc); 582 pl.storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 583 } 584}