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 updateModel) { 168 long topicId = updateModel.getId(); 169 try { 170 checkTopicWriteAccess(topicId); 171 // 172 TopicModelImpl model = fetchTopic(topicId); 173 model.update(updateModel); 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 /** 278 * Fetches from DB and filters READables. No instantiation. 279 */ 280 Iterable<AssociationModelImpl> _getAssociations(String assocTypeUri, long topic1Id, long topic2Id, 281 String roleTypeUri1, String roleTypeUri2) { 282 logger.fine("assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 283 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""); 284 try { 285 return filterReadables(fetchAssociations(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2)); 286 } catch (Exception e) { 287 throw new RuntimeException("Fetching associations between topics " + topic1Id + " and " + topic2Id + 288 " failed (assocTypeUri=\"" + assocTypeUri + "\", roleTypeUri1=\"" + roleTypeUri1 + 289 "\", roleTypeUri2=\"" + roleTypeUri2 + "\")", e); 290 } 291 } 292 293 // --- 294 295 Iterable<Association> getAllAssociations() { 296 return new AssociationIterable(this); 297 } 298 299 long[] getPlayerIds(long assocId) { 300 return fetchPlayerIds(assocId); 301 } 302 303 // --- 304 305 /** 306 * Convenience. 307 */ 308 AssociationImpl createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 309 return createAssociation(mf.newAssociationModel(typeUri, roleModel1, roleModel2)); 310 } 311 312 /** 313 * Creates a new association in the DB. 314 */ 315 AssociationImpl createAssociation(AssociationModelImpl model) { 316 try { 317 em.fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION, model); 318 // 319 model.preCreate(); 320 // 321 // 1) store in DB 322 storeAssociation(model); 323 valueStorage.storeValue(model); 324 createAssociationInstantiation(model.getId(), model.getTypeUri()); 325 // 2) instantiate 326 AssociationImpl assoc = model.instantiate(); 327 // 328 model.postCreate(); 329 // 330 em.fireEvent(CoreEvent.POST_CREATE_ASSOCIATION, assoc); 331 return assoc; 332 } catch (Exception e) { 333 throw new RuntimeException("Creating association failed (" + model + ")", e); 334 } 335 } 336 337 // --- 338 339 void updateAssociation(AssociationModelImpl updateModel) { 340 long assocId = updateModel.getId(); 341 try { 342 checkAssociationWriteAccess(assocId); 343 // 344 AssociationModelImpl model = fetchAssociation(assocId); 345 model.update(updateModel); 346 // 347 // Note: there is no possible POST_UPDATE_ASSOCIATION_REQUEST event to fire here (compare to updateTopic()). 348 // It would be equivalent to POST_UPDATE_ASSOCIATION. Per request exactly one association is updated. 349 // Its childs are always topics (never associations). 350 } catch (Exception e) { 351 throw new RuntimeException("Updating association " + assocId + " failed", e); 352 } 353 } 354 355 void deleteAssociation(long assocId) { 356 try { 357 checkAssociationWriteAccess(assocId); 358 // 359 fetchAssociation(assocId).delete(); 360 } catch (Exception e) { 361 throw new RuntimeException("Deleting association " + assocId + " failed", e); 362 } 363 } 364 365 366 367 // === 368 369 void createTopicInstantiation(long topicId, String topicTypeUri) { 370 try { 371 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 372 mf.newTopicRoleModel(topicTypeUri, "dm4.core.type"), 373 mf.newTopicRoleModel(topicId, "dm4.core.instance")); 374 storeAssociation(assoc); // direct storage calls used here ### explain 375 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 376 createAssociationInstantiation(assoc.getId(), assoc.getTypeUri()); 377 } catch (Exception e) { 378 throw new RuntimeException("Associating topic " + topicId + 379 " with topic type \"" + topicTypeUri + "\" failed", e); 380 } 381 } 382 383 void createAssociationInstantiation(long assocId, String assocTypeUri) { 384 try { 385 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 386 mf.newTopicRoleModel(assocTypeUri, "dm4.core.type"), 387 mf.newAssociationRoleModel(assocId, "dm4.core.instance")); 388 storeAssociation(assoc); // direct storage calls used here ### explain 389 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 390 } catch (Exception e) { 391 throw new RuntimeException("Associating association " + assocId + 392 " with association type \"" + assocTypeUri + "\" failed", e); 393 } 394 } 395 396 397 398 // === Types === 399 400 TopicTypeImpl getTopicType(String uri) { 401 return checkReadAccessAndInstantiate(_getTopicType(uri)); 402 } 403 404 TopicTypeImpl getTopicTypeImplicitly(long topicId) { 405 checkTopicReadAccess(topicId); 406 return _getTopicType(typeUri(topicId)).instantiate(); 407 } 408 409 // --- 410 411 AssociationTypeImpl getAssociationType(String uri) { 412 return checkReadAccessAndInstantiate(_getAssociationType(uri)); 413 } 414 415 AssociationTypeImpl getAssociationTypeImplicitly(long assocId) { 416 checkAssociationReadAccess(assocId); 417 return _getAssociationType(typeUri(assocId)).instantiate(); 418 } 419 420 // --- 421 422 List<TopicType> getAllTopicTypes() { 423 try { 424 List<TopicType> topicTypes = new ArrayList(); 425 for (String uri : getTopicTypeUris()) { 426 topicTypes.add(_getTopicType(uri).instantiate()); 427 } 428 return topicTypes; 429 } catch (Exception e) { 430 throw new RuntimeException("Fetching all topic types failed", e); 431 } 432 } 433 434 List<AssociationType> getAllAssociationTypes() { 435 try { 436 List<AssociationType> assocTypes = new ArrayList(); 437 for (String uri : getAssociationTypeUris()) { 438 assocTypes.add(_getAssociationType(uri).instantiate()); 439 } 440 return assocTypes; 441 } catch (Exception e) { 442 throw new RuntimeException("Fetching all association types failed", e); 443 } 444 } 445 446 // --- 447 448 TopicTypeImpl createTopicType(TopicTypeModelImpl model) { 449 try { 450 em.fireEvent(CoreEvent.PRE_CREATE_TOPIC_TYPE, model); 451 // 452 // store in DB 453 createType(model, URI_PREFIX_TOPIC_TYPE); 454 // 455 TopicTypeImpl topicType = model.instantiate(); 456 em.fireEvent(CoreEvent.INTRODUCE_TOPIC_TYPE, topicType); 457 // 458 return topicType; 459 } catch (Exception e) { 460 throw new RuntimeException("Creating topic type \"" + model.getUri() + "\" failed", e); 461 } 462 } 463 464 AssociationTypeImpl createAssociationType(AssociationTypeModelImpl model) { 465 try { 466 em.fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION_TYPE, model); 467 // 468 // store in DB 469 createType(model, URI_PREFIX_ASSOCIATION_TYPE); 470 // 471 AssociationTypeImpl assocType = model.instantiate(); 472 em.fireEvent(CoreEvent.INTRODUCE_ASSOCIATION_TYPE, assocType); 473 // 474 return assocType; 475 } catch (Exception e) { 476 throw new RuntimeException("Creating association type \"" + model.getUri() + "\" failed", e); 477 } 478 } 479 480 // --- 481 482 void updateTopicType(TopicTypeModelImpl updateModel) { 483 try { 484 // Note: type lookup is by ID. The URI might have changed, the ID does not. 485 // ### FIXME: access control 486 String topicTypeUri = fetchTopic(updateModel.getId()).getUri(); 487 _getTopicType(topicTypeUri).update(updateModel); 488 } catch (Exception e) { 489 throw new RuntimeException("Updating topic type failed (" + updateModel + ")", e); 490 } 491 } 492 493 void updateAssociationType(AssociationTypeModelImpl updateModel) { 494 try { 495 // Note: type lookup is by ID. The URI might have changed, the ID does not. 496 // ### FIXME: access control 497 String assocTypeUri = fetchTopic(updateModel.getId()).getUri(); 498 _getAssociationType(assocTypeUri).update(updateModel); 499 } catch (Exception e) { 500 throw new RuntimeException("Updating association type failed (" + updateModel + ")", e); 501 } 502 } 503 504 // --- 505 506 void deleteTopicType(String topicTypeUri) { 507 try { 508 _getTopicType(topicTypeUri).delete(); // ### TODO: delete view config topics 509 } catch (Exception e) { 510 throw new RuntimeException("Deleting topic type \"" + topicTypeUri + "\" failed", e); 511 } 512 } 513 514 void deleteAssociationType(String assocTypeUri) { 515 try { 516 _getAssociationType(assocTypeUri).delete(); // ### TODO: delete view config topics 517 } catch (Exception e) { 518 throw new RuntimeException("Deleting association type \"" + assocTypeUri + "\" failed", e); 519 } 520 } 521 522 // --- 523 524 Topic createRoleType(TopicModelImpl model) { 525 // check type URI argument 526 String typeUri = model.getTypeUri(); 527 if (typeUri == null) { 528 model.setTypeUri("dm4.core.role_type"); 529 } else { 530 if (!typeUri.equals("dm4.core.role_type")) { 531 throw new IllegalArgumentException("A role type is supposed to be of type \"dm4.core.role_type\" " + 532 "(found: \"" + typeUri + "\")"); 533 } 534 } 535 // 536 return createTopic(model, URI_PREFIX_ROLE_TYPE); 537 } 538 539 // --- 540 541 TopicTypeModelImpl _getTopicType(String uri) { 542 return typeStorage.getTopicType(uri); 543 } 544 545 AssociationTypeModelImpl _getAssociationType(String uri) { 546 return typeStorage.getAssociationType(uri); 547 } 548 549 550 551 // === Generic Object === 552 553 DeepaMehtaObject getObject(long id) { 554 return checkReadAccessAndInstantiate(fetchObject(id)); 555 } 556 557 558 559 // === Properties === 560 561 List<Topic> getTopicsByProperty(String propUri, Object propValue) { 562 return checkReadAccessAndInstantiate(fetchTopicsByProperty(propUri, propValue)); 563 } 564 565 List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) { 566 return checkReadAccessAndInstantiate(fetchTopicsByPropertyRange(propUri, from, to)); 567 } 568 569 List<Association> getAssociationsByProperty(String propUri, Object propValue) { 570 return checkReadAccessAndInstantiate(fetchAssociationsByProperty(propUri, propValue)); 571 } 572 573 List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) { 574 return checkReadAccessAndInstantiate(fetchAssociationsByPropertyRange(propUri, from, to)); 575 } 576 577 // ------------------------------------------------------------------------------------------------- Private Methods 578 579 580 581 // === Access Control / Instantiation === 582 583 // These methods 1) instantiate objects from models, and 2) check the READ permission for each model. 584 // Call these methods when passing objects fetched from the DB to the user. 585 // ### TODO: make these private? 586 587 <O> O checkReadAccessAndInstantiate(DeepaMehtaObjectModelImpl model) { 588 model.checkReadAccess(); 589 return (O) model.instantiate(); 590 } 591 592 <O> List<O> checkReadAccessAndInstantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 593 return instantiate(filterReadables(models)); 594 } 595 596 // --- 597 598 private <M extends DeepaMehtaObjectModelImpl> Iterable<M> filterReadables(Iterable<M> models) { 599 Iterator<? extends DeepaMehtaObjectModelImpl> i = models.iterator(); 600 while (i.hasNext()) { 601 if (!hasReadAccess(i.next())) { 602 i.remove(); 603 } 604 } 605 return models; 606 } 607 608 boolean hasReadAccess(DeepaMehtaObjectModelImpl model) { 609 try { 610 model.checkReadAccess(); 611 return true; 612 } catch (AccessControlException e) { 613 return false; 614 } 615 } 616 617 // --- 618 619 void checkTopicReadAccess(long topicId) { 620 em.fireEvent(CoreEvent.CHECK_TOPIC_READ_ACCESS, topicId); 621 } 622 623 void checkAssociationReadAccess(long assocId) { 624 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_READ_ACCESS, assocId); 625 } 626 627 // --- 628 629 private void checkTopicWriteAccess(long topicId) { 630 em.fireEvent(CoreEvent.CHECK_TOPIC_WRITE_ACCESS, topicId); 631 } 632 633 private void checkAssociationWriteAccess(long assocId) { 634 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_WRITE_ACCESS, assocId); 635 } 636 637 638 639 // === Instantiation === 640 641 <O> List<O> instantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 642 List<O> objects = new ArrayList(); 643 for (DeepaMehtaObjectModelImpl model : models) { 644 objects.add((O) model.instantiate()); 645 } 646 return objects; 647 } 648 649 650 651 // === 652 653 private List<String> getTopicTypeUris() { 654 try { 655 List<String> topicTypeUris = new ArrayList(); 656 // add meta types 657 topicTypeUris.add("dm4.core.topic_type"); 658 topicTypeUris.add("dm4.core.assoc_type"); 659 topicTypeUris.add("dm4.core.meta_type"); 660 // add regular types 661 for (TopicModel topicType : filterReadables(fetchTopics("type_uri", new SimpleValue( 662 "dm4.core.topic_type")))) { 663 topicTypeUris.add(topicType.getUri()); 664 } 665 return topicTypeUris; 666 } catch (Exception e) { 667 throw new RuntimeException("Fetching list of topic type URIs failed", e); 668 } 669 } 670 671 private List<String> getAssociationTypeUris() { 672 try { 673 List<String> assocTypeUris = new ArrayList(); 674 for (TopicModel assocType : filterReadables(fetchTopics("type_uri", new SimpleValue( 675 "dm4.core.assoc_type")))) { 676 assocTypeUris.add(assocType.getUri()); 677 } 678 return assocTypeUris; 679 } catch (Exception e) { 680 throw new RuntimeException("Fetching list of association type URIs failed", e); 681 } 682 } 683 684 // --- 685 686 private void createType(TypeModelImpl model, String uriPrefix) { 687 // Note: the type topic is instantiated explicitly on a `TopicModel` (which is freshly created from the 688 // `TypeModel`). Creating the type topic from the `TypeModel` directly would fail as topic creation implies 689 // topic instantiation, and due to the polymorphic `instantiate()` method a `Type` object would be instantiated 690 // (instead a `Topic` object). But instantiating a type implies per-user type projection, that is removing the 691 // assoc defs not readable by the current user. But at the time the type topic is stored in the DB its assoc 692 // defs are not yet stored, and the readability check would fail. 693 TopicModelImpl typeTopic = mf.newTopicModel(model); 694 createTopic(typeTopic, uriPrefix); // create generic topic 695 // 696 model.id = typeTopic.id; 697 model.uri = typeTopic.uri; 698 // 699 typeStorage.storeType(model); // store type-specific parts 700 } 701 702 private String typeUri(long objectId) { 703 return (String) fetchProperty(objectId, "type_uri"); 704 } 705 706 private void bootstrapTypeCache() { 707 TopicTypeModelImpl metaMetaType = mf.newTopicTypeModel("dm4.core.meta_meta_type", "Meta Meta Type", 708 "dm4.core.text"); 709 metaMetaType.setTypeUri("dm4.core.meta_meta_meta_type"); 710 typeStorage.putInTypeCache(metaMetaType); 711 } 712}