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.RelatedAssociation; 007import de.deepamehta.core.RelatedTopic; 008import de.deepamehta.core.Topic; 009import de.deepamehta.core.TopicType; 010import de.deepamehta.core.model.AssociationModel; 011import de.deepamehta.core.model.AssociationTypeModel; 012import de.deepamehta.core.model.DeepaMehtaObjectModel; 013import de.deepamehta.core.model.RelatedAssociationModel; 014import de.deepamehta.core.model.RelatedTopicModel; 015import de.deepamehta.core.model.RoleModel; 016import de.deepamehta.core.model.SimpleValue; 017import de.deepamehta.core.model.TopicModel; 018import de.deepamehta.core.model.TopicTypeModel; 019import de.deepamehta.core.service.accesscontrol.AccessControlException; 020import de.deepamehta.core.storage.spi.DeepaMehtaStorage; 021 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.List; 026import java.util.logging.Logger; 027 028 029 030public class PersistenceLayer extends StorageDecorator { 031 032 // ------------------------------------------------------------------------------------------------------- Constants 033 034 private static final String URI_PREFIX_TOPIC_TYPE = "domain.project.topic_type_"; 035 private static final String URI_PREFIX_ASSOCIATION_TYPE = "domain.project.assoc_type_"; 036 private static final String URI_PREFIX_ROLE_TYPE = "domain.project.role_type_"; 037 038 // ---------------------------------------------------------------------------------------------- Instance Variables 039 040 TypeStorage typeStorage; 041 ValueStorage valueStorage; 042 043 EventManager em; 044 ModelFactoryImpl mf; 045 046 private final Logger logger = Logger.getLogger(getClass().getName()); 047 048 // ---------------------------------------------------------------------------------------------------- Constructors 049 050 public PersistenceLayer(DeepaMehtaStorage storage) { 051 super(storage); 052 // Note: mf must be initialzed before the type storage is instantiated 053 this.em = new EventManager(); 054 this.mf = (ModelFactoryImpl) storage.getModelFactory(); 055 // 056 this.typeStorage = new TypeStorage(this); 057 this.valueStorage = new ValueStorage(this); 058 // 059 // Note: this is a constructor side effect. This is a cyclic dependency. This is nasty. 060 // ### TODO: explain why we do it. 061 mf.pl = this; 062 // 063 bootstrapTypeCache(); 064 } 065 066 // ----------------------------------------------------------------------------------------- Package Private Methods 067 068 069 070 // === Topics === 071 072 Topic getTopic(long topicId) { 073 try { 074 return checkReadAccessAndInstantiate(fetchTopic(topicId)); 075 } catch (Exception e) { 076 throw new RuntimeException("Fetching topic " + topicId + " failed", e); 077 } 078 } 079 080 TopicImpl getTopicByUri(String uri) { 081 return getTopicByValue("uri", new SimpleValue(uri)); 082 } 083 084 TopicImpl getTopicByValue(String key, SimpleValue value) { 085 try { 086 TopicModelImpl topic = fetchTopic(key, value); 087 return topic != null ? this.<TopicImpl>checkReadAccessAndInstantiate(topic) : null; 088 // Note: inside a conditional operator the type witness is required (at least in Java 6) 089 } catch (Exception e) { 090 throw new RuntimeException("Fetching topic failed (key=\"" + key + "\", value=\"" + value + "\")", e); 091 } 092 } 093 094 List<Topic> getTopicsByValue(String key, SimpleValue value) { 095 try { 096 return checkReadAccessAndInstantiate(fetchTopics(key, value)); 097 } catch (Exception e) { 098 throw new RuntimeException("Fetching topics failed (key=\"" + key + "\", value=\"" + value + "\")", e); 099 } 100 } 101 102 List<Topic> getTopicsByType(String topicTypeUri) { 103 try { 104 return checkReadAccessAndInstantiate(_getTopicType(topicTypeUri).getAllInstances()); 105 } catch (Exception e) { 106 throw new RuntimeException("Fetching topics by type failed (topicTypeUri=\"" + topicTypeUri + "\")", e); 107 } 108 } 109 110 List<Topic> searchTopics(String searchTerm, String fieldUri) { 111 try { 112 return checkReadAccessAndInstantiate(queryTopics(fieldUri, new SimpleValue(searchTerm))); 113 } catch (Exception e) { 114 throw new RuntimeException("Searching topics failed (searchTerm=\"" + searchTerm + "\", fieldUri=\"" + 115 fieldUri + "\")", e); 116 } 117 } 118 119 Iterable<Topic> getAllTopics() { 120 return new TopicIterable(this); 121 } 122 123 // --- 124 125 /** 126 * Convenience. 127 */ 128 TopicImpl createTopic(TopicModelImpl model) { 129 return createTopic(model, null); // uriPrefix=null 130 } 131 132 /** 133 * Creates a new topic in the DB. 134 */ 135 TopicImpl createTopic(TopicModelImpl model, String uriPrefix) { 136 try { 137 em.fireEvent(CoreEvent.PRE_CREATE_TOPIC, model); 138 // 139 model.preCreate(); 140 // 141 // 1) store in DB 142 storeTopic(model); 143 valueStorage.storeValue(model); 144 createTopicInstantiation(model.getId(), model.getTypeUri()); 145 // 2) set default URI 146 // If no URI is given the topic gets a default URI based on its ID, if requested. 147 // Note: this must be done *after* the topic is stored. The ID is not known before. 148 // Note: in case no URI was given: once stored a topic's URI is empty (not null). 149 if (uriPrefix != null && model.getUri().equals("")) { 150 model.updateUri(uriPrefix + model.getId()); // update memory + DB 151 } 152 // 3) instantiate 153 TopicImpl topic = model.instantiate(); 154 // 155 model.postCreate(); 156 // 157 em.fireEvent(CoreEvent.POST_CREATE_TOPIC, topic); 158 return topic; 159 } catch (Exception e) { 160 throw new RuntimeException("Creating topic " + model.getId() + " failed (typeUri=\"" + model.getTypeUri() + 161 "\")", e); 162 } 163 } 164 165 // --- 166 167 void updateTopic(TopicModelImpl newModel) { 168 long topicId = newModel.getId(); 169 try { 170 checkTopicWriteAccess(topicId); 171 // 172 TopicModelImpl model = fetchTopic(topicId); 173 model.update(newModel); 174 // 175 // Note: POST_UPDATE_TOPIC_REQUEST is fired only once per update request. 176 // On the other hand TopicModel's update() method is called multiple times while updating the child topics 177 // (see ChildTopicsModelImpl). 178 em.fireEvent(CoreEvent.POST_UPDATE_TOPIC_REQUEST, model.instantiate()); 179 } catch (Exception e) { 180 throw new RuntimeException("Updating topic " + topicId + " failed", e); 181 } 182 } 183 184 void deleteTopic(long topicId) { 185 try { 186 checkTopicWriteAccess(topicId); 187 // 188 fetchTopic(topicId).delete(); 189 } catch (Exception e) { 190 throw new RuntimeException("Deleting topic " + topicId + " failed", e); 191 } 192 } 193 194 195 196 // === Associations === 197 198 Association getAssociation(long assocId) { 199 try { 200 return checkReadAccessAndInstantiate(fetchAssociation(assocId)); 201 } catch (Exception e) { 202 throw new RuntimeException("Fetching association " + assocId + " failed", e); 203 } 204 } 205 206 Association getAssociationByValue(String key, SimpleValue value) { 207 try { 208 AssociationModelImpl assoc = fetchAssociation(key, value); 209 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 210 // Note: inside a conditional operator the type witness is required (at least in Java 6) 211 } catch (Exception e) { 212 throw new RuntimeException("Fetching association failed (key=\"" + key + "\", value=\"" + value + "\")", e); 213 } 214 } 215 216 List<Association> getAssociationsByValue(String key, SimpleValue value) { 217 try { 218 return checkReadAccessAndInstantiate(fetchAssociations(key, value)); 219 } catch (Exception e) { 220 throw new RuntimeException("Fetching associationss failed (key=\"" + key + "\", value=\"" + value + "\")", 221 e); 222 } 223 } 224 225 Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id, String roleTypeUri1, 226 String roleTypeUri2) { 227 String info = "assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 228 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""; 229 try { 230 AssociationModelImpl assoc = fetchAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2); 231 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 232 // Note: inside a conditional operator the type witness is required (at least in Java 6) 233 } catch (Exception e) { 234 throw new RuntimeException("Fetching association failed (" + info + ")", e); 235 } 236 } 237 238 Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId, 239 String topicRoleTypeUri, String assocRoleTypeUri) { 240 String info = "assocTypeUri=\"" + assocTypeUri + "\", topicId=" + topicId + ", assocId=" + assocId + 241 ", topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\""; 242 logger.info(info); 243 try { 244 AssociationModelImpl assoc = fetchAssociationBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, 245 topicRoleTypeUri, assocRoleTypeUri); 246 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 247 // Note: inside a conditional operator the type witness is required (at least in Java 6) 248 } catch (Exception e) { 249 throw new RuntimeException("Fetching association failed (" + info + ")", e); 250 } 251 } 252 253 // --- 254 255 List<Association> getAssociationsByType(String assocTypeUri) { 256 try { 257 return checkReadAccessAndInstantiate(_getAssociationType(assocTypeUri).getAllInstances()); 258 } catch (Exception e) { 259 throw new RuntimeException("Fetching associations by type failed (assocTypeUri=\"" + assocTypeUri + "\")", 260 e); 261 } 262 } 263 264 List<Association> getAssociations(long topic1Id, long topic2Id) { 265 return getAssociations(null, topic1Id, topic2Id); // assocTypeUri=null 266 } 267 268 List<Association> getAssociations(String assocTypeUri, long topic1Id, long topic2Id) { 269 return getAssociations(assocTypeUri, topic1Id, topic2Id, null, null); // roleTypeUri1=null, roleTypeUri2=null 270 } 271 272 List<Association> getAssociations(String assocTypeUri, long topic1Id, long topic2Id, String roleTypeUri1, 273 String roleTypeUri2) { 274 return instantiate(_getAssociations(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2)); 275 } 276 277 Iterable<AssociationModelImpl> _getAssociations(String assocTypeUri, long topic1Id, long topic2Id, 278 String roleTypeUri1, String roleTypeUri2) { 279 logger.fine("assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 280 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""); 281 try { 282 return filterReadables(fetchAssociations(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2)); 283 } catch (Exception e) { 284 throw new RuntimeException("Fetching associations between topics " + topic1Id + " and " + topic2Id + 285 " failed (assocTypeUri=\"" + assocTypeUri + "\", roleTypeUri1=\"" + roleTypeUri1 + 286 "\", roleTypeUri2=\"" + roleTypeUri2 + "\")", e); 287 } 288 } 289 290 // --- 291 292 Iterable<Association> getAllAssociations() { 293 return new AssociationIterable(this); 294 } 295 296 long[] getPlayerIds(long assocId) { 297 return fetchPlayerIds(assocId); 298 } 299 300 // --- 301 302 /** 303 * Convenience. 304 */ 305 AssociationImpl createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 306 return createAssociation(mf.newAssociationModel(typeUri, roleModel1, roleModel2)); 307 } 308 309 /** 310 * Creates a new association in the DB. 311 */ 312 AssociationImpl createAssociation(AssociationModelImpl model) { 313 try { 314 em.fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION, model); 315 // 316 model.preCreate(); 317 // 318 // 1) store in DB 319 storeAssociation(model); 320 valueStorage.storeValue(model); 321 createAssociationInstantiation(model.getId(), model.getTypeUri()); 322 // 2) instantiate 323 AssociationImpl assoc = model.instantiate(); 324 // 325 model.postCreate(); 326 // 327 em.fireEvent(CoreEvent.POST_CREATE_ASSOCIATION, assoc); 328 return assoc; 329 } catch (Exception e) { 330 throw new RuntimeException("Creating association failed (" + model + ")", e); 331 } 332 } 333 334 // --- 335 336 void updateAssociation(AssociationModelImpl newModel) { 337 long assocId = newModel.getId(); 338 try { 339 checkAssociationWriteAccess(assocId); 340 // 341 AssociationModelImpl model = fetchAssociation(assocId); 342 model.update(newModel); 343 // 344 // Note: there is no possible POST_UPDATE_ASSOCIATION_REQUEST event to fire here (compare to updateTopic()). 345 // It would be equivalent to POST_UPDATE_ASSOCIATION. Per request exactly one association is updated. 346 // Its childs are always topics (never associations). 347 } catch (Exception e) { 348 throw new RuntimeException("Updating association " + assocId + " failed", e); 349 } 350 } 351 352 void deleteAssociation(long assocId) { 353 try { 354 checkAssociationWriteAccess(assocId); 355 // 356 fetchAssociation(assocId).delete(); 357 } catch (Exception e) { 358 throw new RuntimeException("Deleting association " + assocId + " failed", e); 359 } 360 } 361 362 363 364 // === 365 366 void createTopicInstantiation(long topicId, String topicTypeUri) { 367 try { 368 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 369 mf.newTopicRoleModel(topicTypeUri, "dm4.core.type"), 370 mf.newTopicRoleModel(topicId, "dm4.core.instance")); 371 storeAssociation(assoc); // direct storage calls used here ### explain 372 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 373 createAssociationInstantiation(assoc.getId(), assoc.getTypeUri()); 374 } catch (Exception e) { 375 throw new RuntimeException("Associating topic " + topicId + 376 " with topic type \"" + topicTypeUri + "\" failed", e); 377 } 378 } 379 380 void createAssociationInstantiation(long assocId, String assocTypeUri) { 381 try { 382 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 383 mf.newTopicRoleModel(assocTypeUri, "dm4.core.type"), 384 mf.newAssociationRoleModel(assocId, "dm4.core.instance")); 385 storeAssociation(assoc); // direct storage calls used here ### explain 386 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 387 } catch (Exception e) { 388 throw new RuntimeException("Associating association " + assocId + 389 " with association type \"" + assocTypeUri + "\" failed", e); 390 } 391 } 392 393 394 395 // === Types === 396 397 TopicType getTopicType(String uri) { 398 TopicTypeModelImpl topicType = _getTopicType(uri); 399 if (!uri.equals("dm4.core.meta_meta_type")) { 400 checkReadAccess(topicType); 401 } 402 return topicType.instantiate(); 403 } 404 405 TopicType getTopicTypeImplicitly(long topicId) { 406 checkTopicReadAccess(topicId); 407 return _getTopicType(typeUri(topicId)).instantiate(); 408 } 409 410 // --- 411 412 AssociationType getAssociationType(String uri) { 413 return checkReadAccessAndInstantiate(_getAssociationType(uri)); 414 } 415 416 AssociationType getAssociationTypeImplicitly(long assocId) { 417 checkAssociationReadAccess(assocId); 418 return _getAssociationType(typeUri(assocId)).instantiate(); 419 } 420 421 // --- 422 423 List<TopicType> getAllTopicTypes() { 424 try { 425 List<TopicType> topicTypes = new ArrayList(); 426 for (String uri : getTopicTypeUris()) { 427 topicTypes.add(_getTopicType(uri).instantiate()); 428 } 429 return topicTypes; 430 } catch (Exception e) { 431 throw new RuntimeException("Fetching all topic types failed", e); 432 } 433 } 434 435 List<AssociationType> getAllAssociationTypes() { 436 try { 437 List<AssociationType> assocTypes = new ArrayList(); 438 for (String uri : getAssociationTypeUris()) { 439 assocTypes.add(_getAssociationType(uri).instantiate()); 440 } 441 return assocTypes; 442 } catch (Exception e) { 443 throw new RuntimeException("Fetching all association types failed", e); 444 } 445 } 446 447 // --- 448 449 TopicType createTopicType(TopicTypeModelImpl model) { 450 try { 451 // store in DB 452 createTypeTopic(model, URI_PREFIX_TOPIC_TYPE); 453 // 454 TopicType topicType = model.instantiate(); 455 em.fireEvent(CoreEvent.INTRODUCE_TOPIC_TYPE, topicType); 456 // 457 return topicType; 458 } catch (Exception e) { 459 throw new RuntimeException("Creating topic type \"" + model.getUri() + "\" failed", e); 460 } 461 } 462 463 AssociationType createAssociationType(AssociationTypeModelImpl model) { 464 try { 465 // store in DB 466 createTypeTopic(model, URI_PREFIX_ASSOCIATION_TYPE); 467 // 468 AssociationType assocType = model.instantiate(); 469 em.fireEvent(CoreEvent.INTRODUCE_ASSOCIATION_TYPE, assocType); 470 // 471 return assocType; 472 } catch (Exception e) { 473 throw new RuntimeException("Creating association type \"" + model.getUri() + "\" failed", e); 474 } 475 } 476 477 // --- 478 479 void updateTopicType(TopicTypeModelImpl newModel) { 480 try { 481 // Note: type lookup is by ID. The URI might have changed, the ID does not. 482 // ### FIXME: access control 483 String topicTypeUri = fetchTopic(newModel.getId()).getUri(); 484 _getTopicType(topicTypeUri).update(newModel); 485 } catch (Exception e) { 486 throw new RuntimeException("Updating topic type failed (" + newModel + ")", e); 487 } 488 } 489 490 void updateAssociationType(AssociationTypeModelImpl newModel) { 491 try { 492 // Note: type lookup is by ID. The URI might have changed, the ID does not. 493 // ### FIXME: access control 494 String assocTypeUri = fetchTopic(newModel.getId()).getUri(); 495 _getAssociationType(assocTypeUri).update(newModel); 496 } catch (Exception e) { 497 throw new RuntimeException("Updating association type failed (" + newModel + ")", e); 498 } 499 } 500 501 // --- 502 503 void deleteTopicType(String topicTypeUri) { 504 try { 505 _getTopicType(topicTypeUri).delete(); // ### TODO: delete view config topics 506 } catch (Exception e) { 507 throw new RuntimeException("Deleting topic type \"" + topicTypeUri + "\" failed", e); 508 } 509 } 510 511 void deleteAssociationType(String assocTypeUri) { 512 try { 513 _getAssociationType(assocTypeUri).delete(); // ### TODO: delete view config topics 514 } catch (Exception e) { 515 throw new RuntimeException("Deleting association type \"" + assocTypeUri + "\" failed", e); 516 } 517 } 518 519 // --- 520 521 Topic createRoleType(TopicModelImpl model) { 522 // check type URI argument 523 String typeUri = model.getTypeUri(); 524 if (typeUri == null) { 525 model.setTypeUri("dm4.core.role_type"); 526 } else { 527 if (!typeUri.equals("dm4.core.role_type")) { 528 throw new IllegalArgumentException("A role type is supposed to be of type \"dm4.core.role_type\" " + 529 "(found: \"" + typeUri + "\")"); 530 } 531 } 532 // 533 return createTopic(model, URI_PREFIX_ROLE_TYPE); 534 } 535 536 // --- 537 538 TopicTypeModelImpl _getTopicType(String uri) { 539 return typeStorage.getTopicType(uri); 540 } 541 542 AssociationTypeModelImpl _getAssociationType(String uri) { 543 return typeStorage.getAssociationType(uri); 544 } 545 546 547 548 // === Generic Object === 549 550 DeepaMehtaObject getObject(long id) { 551 return checkReadAccessAndInstantiate(fetchObject(id)); 552 } 553 554 555 556 // === Properties === 557 558 List<Topic> getTopicsByProperty(String propUri, Object propValue) { 559 return checkReadAccessAndInstantiate(fetchTopicsByProperty(propUri, propValue)); 560 } 561 562 List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) { 563 return checkReadAccessAndInstantiate(fetchTopicsByPropertyRange(propUri, from, to)); 564 } 565 566 List<Association> getAssociationsByProperty(String propUri, Object propValue) { 567 return checkReadAccessAndInstantiate(fetchAssociationsByProperty(propUri, propValue)); 568 } 569 570 List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) { 571 return checkReadAccessAndInstantiate(fetchAssociationsByPropertyRange(propUri, from, to)); 572 } 573 574 // ------------------------------------------------------------------------------------------------- Private Methods 575 576 577 578 // === Access Control / Instantiation === 579 580 // These methods 1) instantiate objects from models, and 2) check the READ permission for each model. 581 // Call these methods when passing objects fetched from the DB to the user. 582 // ### TODO: make these private? 583 584 <O> O checkReadAccessAndInstantiate(DeepaMehtaObjectModelImpl model) { 585 checkReadAccess(model); 586 return (O) model.instantiate(); 587 } 588 589 <O> List<O> checkReadAccessAndInstantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 590 return instantiate(filterReadables(models)); 591 } 592 593 // --- 594 595 private <M extends DeepaMehtaObjectModelImpl> Iterable<M> filterReadables(Iterable<M> models) { 596 Iterator<? extends DeepaMehtaObjectModelImpl> i = models.iterator(); 597 while (i.hasNext()) { 598 if (!hasReadAccess(i.next())) { 599 i.remove(); 600 } 601 } 602 return models; 603 } 604 605 boolean hasReadAccess(DeepaMehtaObjectModelImpl model) { 606 try { 607 checkReadAccess(model); 608 return true; 609 } catch (AccessControlException e) { 610 return false; 611 } 612 } 613 614 /** 615 * @throws AccessControlException 616 */ 617 private void checkReadAccess(DeepaMehtaObjectModelImpl model) { 618 em.fireEvent(model.getReadAccessEvent(), model.getId()); 619 } 620 621 // --- 622 623 private void checkTopicReadAccess(long topicId) { 624 em.fireEvent(CoreEvent.CHECK_TOPIC_READ_ACCESS, topicId); 625 } 626 627 private void checkAssociationReadAccess(long assocId) { 628 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_READ_ACCESS, assocId); 629 } 630 631 // --- 632 633 private void checkTopicWriteAccess(long topicId) { 634 em.fireEvent(CoreEvent.CHECK_TOPIC_WRITE_ACCESS, topicId); 635 } 636 637 private void checkAssociationWriteAccess(long assocId) { 638 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_WRITE_ACCESS, assocId); 639 } 640 641 642 643 // === Instantiation === 644 645 <O> List<O> instantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 646 List<O> objects = new ArrayList(); 647 for (DeepaMehtaObjectModelImpl model : models) { 648 objects.add((O) model.instantiate()); 649 } 650 return objects; 651 } 652 653 654 655 // === 656 657 private List<String> getTopicTypeUris() { 658 try { 659 List<String> topicTypeUris = new ArrayList(); 660 // add meta types 661 topicTypeUris.add("dm4.core.topic_type"); 662 topicTypeUris.add("dm4.core.assoc_type"); 663 topicTypeUris.add("dm4.core.meta_type"); 664 topicTypeUris.add("dm4.core.meta_meta_type"); 665 // add regular types 666 for (TopicModel topicType : filterReadables(fetchTopics("type_uri", new SimpleValue( 667 "dm4.core.topic_type")))) { 668 topicTypeUris.add(topicType.getUri()); 669 } 670 return topicTypeUris; 671 } catch (Exception e) { 672 throw new RuntimeException("Fetching list of topic type URIs failed", e); 673 } 674 } 675 676 private List<String> getAssociationTypeUris() { 677 try { 678 List<String> assocTypeUris = new ArrayList(); 679 for (TopicModel assocType : filterReadables(fetchTopics("type_uri", new SimpleValue( 680 "dm4.core.assoc_type")))) { 681 assocTypeUris.add(assocType.getUri()); 682 } 683 return assocTypeUris; 684 } catch (Exception e) { 685 throw new RuntimeException("Fetching list of association type URIs failed", e); 686 } 687 } 688 689 // --- 690 691 private void createTypeTopic(TypeModelImpl model, String uriPrefix) { 692 // Note: the type topic is instantiated explicitly on a `TopicModel` (which is freshly created from the 693 // `TypeModel`). Creating the type topic from the `TypeModel` directly would fail as topic creation implies 694 // topic instantiation, and due to the polymorphic `instantiate()` method a `Type` object would be instantiated 695 // (instead a `Topic` object). But instantiating a type newly implies per-user type projection, that is removing 696 // the assoc defs not readable by the current user. But at the time the type topic is stored in the DB its assoc 697 // defs are not yet stored, and the readability check would fail. 698 TopicModelImpl typeTopic = mf.newTopicModel(model); 699 createTopic(typeTopic, uriPrefix); // create generic topic 700 // 701 model.id = typeTopic.id; 702 model.uri = typeTopic.uri; 703 // 704 typeStorage.storeType(model); // store type-specific parts 705 } 706 707 private String typeUri(long objectId) { 708 return (String) fetchProperty(objectId, "type_uri"); 709 } 710 711 private void bootstrapTypeCache() { 712 TopicTypeModelImpl metaMetaType = mf.newTopicTypeModel("dm4.core.meta_meta_type", "Meta Meta Type", 713 "dm4.core.text"); 714 metaMetaType.setTypeUri("dm4.core.meta_meta_meta_type"); 715 typeStorage.putInTypeCache(metaMetaType); 716 } 717}