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