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.Topic; 007import de.deepamehta.core.TopicType; 008import de.deepamehta.core.model.AssociationModel; 009import de.deepamehta.core.model.AssociationTypeModel; 010import de.deepamehta.core.model.DeepaMehtaObjectModel; 011import de.deepamehta.core.model.RelatedAssociationModel; 012import de.deepamehta.core.model.RelatedTopicModel; 013import de.deepamehta.core.model.RoleModel; 014import de.deepamehta.core.model.SimpleValue; 015import de.deepamehta.core.model.TopicModel; 016import de.deepamehta.core.model.TopicTypeModel; 017import de.deepamehta.core.service.accesscontrol.AccessControlException; 018import de.deepamehta.core.storage.spi.DeepaMehtaStorage; 019 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023import java.util.logging.Logger; 024 025 026 027/** 028 * Adds access control on top of vendor specific storage. 029 */ 030public final 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 try { 169 TopicModelImpl model = fetchTopic(updateModel.getId()); 170 updateTopic(model, updateModel); 171 // 172 // Note: POST_UPDATE_TOPIC_REQUEST is fired only once per update request. 173 // On the other hand TopicModel's update() method is called multiple times while updating the child topics 174 // (see ChildTopicsModelImpl). 175 em.fireEvent(CoreEvent.POST_UPDATE_TOPIC_REQUEST, model.instantiate()); 176 } catch (Exception e) { 177 throw new RuntimeException("Updating topic " + updateModel.getId() + " failed", e); 178 } 179 } 180 181 void updateTopic(TopicModelImpl topic, TopicModelImpl updateModel) { 182 try { 183 topic.checkWriteAccess(); 184 topic.update(updateModel); 185 } catch (Exception e) { 186 throw new RuntimeException("Updating topic " + topic.getId() + " failed", e); 187 } 188 } 189 190 // --- 191 192 void deleteTopic(long topicId) { 193 deleteTopic(fetchTopic(topicId)); 194 } 195 196 void deleteTopic(TopicModelImpl topic) { 197 try { 198 topic.checkWriteAccess(); 199 topic.delete(); 200 } catch (Exception e) { 201 throw new RuntimeException("Deleting topic " + topic.getId() + " failed", e); 202 } 203 } 204 205 206 207 // === Associations === 208 209 Association getAssociation(long assocId) { 210 try { 211 return checkReadAccessAndInstantiate(fetchAssociation(assocId)); 212 } catch (Exception e) { 213 throw new RuntimeException("Fetching association " + assocId + " failed", e); 214 } 215 } 216 217 Association getAssociationByValue(String key, SimpleValue value) { 218 try { 219 AssociationModelImpl assoc = fetchAssociation(key, value); 220 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 221 // Note: inside a conditional operator the type witness is required (at least in Java 6) 222 } catch (Exception e) { 223 throw new RuntimeException("Fetching association failed (key=\"" + key + "\", value=\"" + value + "\")", e); 224 } 225 } 226 227 List<Association> getAssociationsByValue(String key, SimpleValue value) { 228 try { 229 return checkReadAccessAndInstantiate(fetchAssociations(key, value)); 230 } catch (Exception e) { 231 throw new RuntimeException("Fetching associationss failed (key=\"" + key + "\", value=\"" + value + "\")", 232 e); 233 } 234 } 235 236 Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id, String roleTypeUri1, 237 String roleTypeUri2) { 238 String info = "assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 239 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""; 240 try { 241 AssociationModelImpl assoc = fetchAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2); 242 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 243 // Note: inside a conditional operator the type witness is required (at least in Java 6) 244 } catch (Exception e) { 245 throw new RuntimeException("Fetching association failed (" + info + ")", e); 246 } 247 } 248 249 Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId, 250 String topicRoleTypeUri, String assocRoleTypeUri) { 251 String info = "assocTypeUri=\"" + assocTypeUri + "\", topicId=" + topicId + ", assocId=" + assocId + 252 ", topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\""; 253 logger.info(info); 254 try { 255 AssociationModelImpl assoc = fetchAssociationBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, 256 topicRoleTypeUri, assocRoleTypeUri); 257 return assoc != null ? this.<Association>checkReadAccessAndInstantiate(assoc) : null; 258 // Note: inside a conditional operator the type witness is required (at least in Java 6) 259 } catch (Exception e) { 260 throw new RuntimeException("Fetching association failed (" + info + ")", e); 261 } 262 } 263 264 // --- 265 266 List<Association> getAssociationsByType(String assocTypeUri) { 267 try { 268 return checkReadAccessAndInstantiate(_getAssociationType(assocTypeUri).getAllInstances()); 269 } catch (Exception e) { 270 throw new RuntimeException("Fetching associations by type failed (assocTypeUri=\"" + assocTypeUri + "\")", 271 e); 272 } 273 } 274 275 List<Association> getAssociations(long topic1Id, long topic2Id) { 276 return getAssociations(null, topic1Id, topic2Id); // assocTypeUri=null 277 } 278 279 List<Association> getAssociations(String assocTypeUri, long topic1Id, long topic2Id) { 280 return getAssociations(assocTypeUri, topic1Id, topic2Id, null, null); // roleTypeUri1=null, roleTypeUri2=null 281 } 282 283 List<Association> getAssociations(String assocTypeUri, long topic1Id, long topic2Id, String roleTypeUri1, 284 String roleTypeUri2) { 285 return instantiate(_getAssociations(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2)); 286 } 287 288 /** 289 * Fetches from DB and filters READables. No instantiation. 290 */ 291 Iterable<AssociationModelImpl> _getAssociations(String assocTypeUri, long topic1Id, long topic2Id, 292 String roleTypeUri1, String roleTypeUri2) { 293 logger.fine("assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + 294 ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\""); 295 try { 296 return filterReadables(fetchAssociations(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2)); 297 } catch (Exception e) { 298 throw new RuntimeException("Fetching associations between topics " + topic1Id + " and " + topic2Id + 299 " failed (assocTypeUri=\"" + assocTypeUri + "\", roleTypeUri1=\"" + roleTypeUri1 + 300 "\", roleTypeUri2=\"" + roleTypeUri2 + "\")", e); 301 } 302 } 303 304 // --- 305 306 Iterable<Association> getAllAssociations() { 307 return new AssociationIterable(this); 308 } 309 310 long[] getPlayerIds(long assocId) { 311 return fetchPlayerIds(assocId); 312 } 313 314 // --- 315 316 /** 317 * Convenience. 318 */ 319 AssociationImpl createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 320 return createAssociation(mf.newAssociationModel(typeUri, roleModel1, roleModel2)); 321 } 322 323 /** 324 * Creates a new association in the DB. 325 */ 326 AssociationImpl createAssociation(AssociationModelImpl model) { 327 try { 328 em.fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION, model); 329 // 330 model.preCreate(); 331 // 332 // 1) store in DB 333 storeAssociation(model); 334 valueStorage.storeValue(model); 335 createAssociationInstantiation(model.getId(), model.getTypeUri()); 336 // 2) instantiate 337 AssociationImpl assoc = model.instantiate(); 338 // 339 model.postCreate(); 340 // 341 em.fireEvent(CoreEvent.POST_CREATE_ASSOCIATION, assoc); 342 return assoc; 343 } catch (Exception e) { 344 throw new RuntimeException("Creating association failed (" + model + ")", e); 345 } 346 } 347 348 // --- 349 350 void updateAssociation(AssociationModelImpl updateModel) { 351 try { 352 AssociationModelImpl model = fetchAssociation(updateModel.getId()); 353 updateAssociation(model, updateModel); 354 // 355 // Note: there is no possible POST_UPDATE_ASSOCIATION_REQUEST event to fire here (compare to updateTopic()). 356 // It would be equivalent to POST_UPDATE_ASSOCIATION. Per request exactly one association is updated. 357 // Its childs are always topics (never associations). 358 } catch (Exception e) { 359 throw new RuntimeException("Updating association " + updateModel.getId() + " failed", e); 360 } 361 } 362 363 void updateAssociation(AssociationModelImpl assoc, AssociationModelImpl updateModel) { 364 try { 365 checkAssociationWriteAccess(assoc.getId()); 366 assoc.update(updateModel); 367 } catch (Exception e) { 368 throw new RuntimeException("Updating association " + assoc.getId() + " failed", e); 369 } 370 } 371 372 // --- 373 374 void deleteAssociation(long assocId) { 375 deleteAssociation(fetchAssociation(assocId)); 376 } 377 378 void deleteAssociation(AssociationModelImpl assoc) { 379 try { 380 checkAssociationWriteAccess(assoc.getId()); 381 assoc.delete(); 382 } catch (Exception e) { 383 throw new RuntimeException("Deleting association " + assoc.getId() + " failed", e); 384 } 385 } 386 387 388 389 // === 390 391 void createTopicInstantiation(long topicId, String topicTypeUri) { 392 try { 393 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 394 mf.newTopicRoleModel(topicTypeUri, "dm4.core.type"), 395 mf.newTopicRoleModel(topicId, "dm4.core.instance")); 396 storeAssociation(assoc); // direct storage calls used here ### explain 397 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 398 createAssociationInstantiation(assoc.getId(), assoc.getTypeUri()); 399 } catch (Exception e) { 400 throw new RuntimeException("Associating topic " + topicId + 401 " with topic type \"" + topicTypeUri + "\" failed", e); 402 } 403 } 404 405 void createAssociationInstantiation(long assocId, String assocTypeUri) { 406 try { 407 AssociationModel assoc = mf.newAssociationModel("dm4.core.instantiation", 408 mf.newTopicRoleModel(assocTypeUri, "dm4.core.type"), 409 mf.newAssociationRoleModel(assocId, "dm4.core.instance")); 410 storeAssociation(assoc); // direct storage calls used here ### explain 411 storeAssociationValue(assoc.getId(), assoc.getSimpleValue()); 412 } catch (Exception e) { 413 throw new RuntimeException("Associating association " + assocId + 414 " with association type \"" + assocTypeUri + "\" failed", e); 415 } 416 } 417 418 419 420 // === Types === 421 422 TopicTypeImpl getTopicType(String uri) { 423 return checkReadAccessAndInstantiate(_getTopicType(uri)); 424 } 425 426 TopicTypeImpl getTopicTypeImplicitly(long topicId) { 427 checkTopicReadAccess(topicId); 428 return _getTopicType(typeUri(topicId)).instantiate(); 429 } 430 431 // --- 432 433 AssociationTypeImpl getAssociationType(String uri) { 434 return checkReadAccessAndInstantiate(_getAssociationType(uri)); 435 } 436 437 AssociationTypeImpl getAssociationTypeImplicitly(long assocId) { 438 checkAssociationReadAccess(assocId); 439 return _getAssociationType(typeUri(assocId)).instantiate(); 440 } 441 442 // --- 443 444 List<TopicType> getAllTopicTypes() { 445 try { 446 List<TopicType> topicTypes = new ArrayList(); 447 for (String uri : getTopicTypeUris()) { 448 topicTypes.add(_getTopicType(uri).instantiate()); 449 } 450 return topicTypes; 451 } catch (Exception e) { 452 throw new RuntimeException("Fetching all topic types failed", e); 453 } 454 } 455 456 List<AssociationType> getAllAssociationTypes() { 457 try { 458 List<AssociationType> assocTypes = new ArrayList(); 459 for (String uri : getAssociationTypeUris()) { 460 assocTypes.add(_getAssociationType(uri).instantiate()); 461 } 462 return assocTypes; 463 } catch (Exception e) { 464 throw new RuntimeException("Fetching all association types failed", e); 465 } 466 } 467 468 // --- 469 470 TopicTypeImpl createTopicType(TopicTypeModelImpl model) { 471 try { 472 em.fireEvent(CoreEvent.PRE_CREATE_TOPIC_TYPE, model); 473 // 474 // store in DB 475 createType(model, URI_PREFIX_TOPIC_TYPE); 476 // 477 TopicTypeImpl topicType = model.instantiate(); 478 em.fireEvent(CoreEvent.INTRODUCE_TOPIC_TYPE, topicType); 479 // 480 return topicType; 481 } catch (Exception e) { 482 throw new RuntimeException("Creating topic type \"" + model.getUri() + "\" failed", e); 483 } 484 } 485 486 AssociationTypeImpl createAssociationType(AssociationTypeModelImpl model) { 487 try { 488 em.fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION_TYPE, model); 489 // 490 // store in DB 491 createType(model, URI_PREFIX_ASSOCIATION_TYPE); 492 // 493 AssociationTypeImpl assocType = model.instantiate(); 494 em.fireEvent(CoreEvent.INTRODUCE_ASSOCIATION_TYPE, assocType); 495 // 496 return assocType; 497 } catch (Exception e) { 498 throw new RuntimeException("Creating association type \"" + model.getUri() + "\" failed", e); 499 } 500 } 501 502 // --- 503 504 void updateTopicType(TopicTypeModelImpl updateModel) { 505 try { 506 // Note: type lookup is by ID. The URI might have changed, the ID does not. 507 TopicModelImpl topic = fetchTopic(updateModel.getId()); 508 topic.checkWriteAccess(); 509 _getTopicType(topic.getUri()).update(updateModel); 510 } catch (Exception e) { 511 throw new RuntimeException("Updating topic type failed (" + updateModel + ")", e); 512 } 513 } 514 515 void updateAssociationType(AssociationTypeModelImpl updateModel) { 516 try { 517 // Note: type lookup is by ID. The URI might have changed, the ID does not. 518 TopicModelImpl topic = fetchTopic(updateModel.getId()); 519 topic.checkWriteAccess(); 520 _getAssociationType(topic.getUri()).update(updateModel); 521 } catch (Exception e) { 522 throw new RuntimeException("Updating association type failed (" + updateModel + ")", e); 523 } 524 } 525 526 // --- 527 528 void deleteTopicType(String topicTypeUri) { 529 try { 530 TypeModelImpl type = _getTopicType(topicTypeUri); 531 type.checkWriteAccess(); 532 type.delete(); 533 // ### 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 TypeModelImpl type = _getAssociationType(assocTypeUri); 542 type.checkWriteAccess(); 543 type.delete(); 544 // ### TODO: delete view config topics 545 } catch (Exception e) { 546 throw new RuntimeException("Deleting association type \"" + assocTypeUri + "\" failed", e); 547 } 548 } 549 550 // --- 551 552 Topic createRoleType(TopicModelImpl model) { 553 // check type URI argument 554 String typeUri = model.getTypeUri(); 555 if (typeUri == null) { 556 model.setTypeUri("dm4.core.role_type"); 557 } else { 558 if (!typeUri.equals("dm4.core.role_type")) { 559 throw new IllegalArgumentException("A role type is supposed to be of type \"dm4.core.role_type\" " + 560 "(found: \"" + typeUri + "\")"); 561 } 562 } 563 // 564 return createTopic(model, URI_PREFIX_ROLE_TYPE); 565 } 566 567 // --- 568 569 /** 570 * Type cache direct access. No permission check. 571 */ 572 TopicTypeModelImpl _getTopicType(String uri) { 573 return typeStorage.getTopicType(uri); 574 } 575 576 /** 577 * Type cache direct access. No permission check. 578 */ 579 AssociationTypeModelImpl _getAssociationType(String uri) { 580 return typeStorage.getAssociationType(uri); 581 } 582 583 584 585 // === Generic Object === 586 587 DeepaMehtaObject getObject(long id) { 588 return checkReadAccessAndInstantiate(fetchObject(id)); 589 } 590 591 592 593 // === Properties === 594 595 List<Topic> getTopicsByProperty(String propUri, Object propValue) { 596 return checkReadAccessAndInstantiate(fetchTopicsByProperty(propUri, propValue)); 597 } 598 599 List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) { 600 return checkReadAccessAndInstantiate(fetchTopicsByPropertyRange(propUri, from, to)); 601 } 602 603 List<Association> getAssociationsByProperty(String propUri, Object propValue) { 604 return checkReadAccessAndInstantiate(fetchAssociationsByProperty(propUri, propValue)); 605 } 606 607 List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) { 608 return checkReadAccessAndInstantiate(fetchAssociationsByPropertyRange(propUri, from, to)); 609 } 610 611 // ------------------------------------------------------------------------------------------------- Private Methods 612 613 614 615 // === Access Control / Instantiation === 616 617 // These methods 1) instantiate objects from models, and 2) check the READ permission for each model. 618 // Call these methods when passing objects fetched from the DB to the user. 619 // ### TODO: make these private? 620 621 <O> O checkReadAccessAndInstantiate(DeepaMehtaObjectModelImpl model) { 622 model.checkReadAccess(); 623 return (O) model.instantiate(); 624 } 625 626 <O> List<O> checkReadAccessAndInstantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 627 return instantiate(filterReadables(models)); 628 } 629 630 // --- 631 632 private <M extends DeepaMehtaObjectModelImpl> Iterable<M> filterReadables(Iterable<M> models) { 633 Iterator<? extends DeepaMehtaObjectModelImpl> i = models.iterator(); 634 while (i.hasNext()) { 635 if (!hasReadAccess(i.next())) { 636 i.remove(); 637 } 638 } 639 return models; 640 } 641 642 boolean hasReadAccess(DeepaMehtaObjectModelImpl model) { 643 try { 644 model.checkReadAccess(); 645 return true; 646 } catch (AccessControlException e) { 647 return false; 648 } 649 } 650 651 // --- 652 653 /** 654 * @throws AccessControlException if the current user has no permission. 655 */ 656 void checkTopicReadAccess(long topicId) { 657 em.fireEvent(CoreEvent.CHECK_TOPIC_READ_ACCESS, topicId); 658 } 659 660 /** 661 * @throws AccessControlException if the current user has no permission. 662 */ 663 void checkAssociationReadAccess(long assocId) { 664 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_READ_ACCESS, assocId); 665 } 666 667 // --- 668 669 /** 670 * @throws AccessControlException if the current user has no permission. 671 */ 672 void checkTopicWriteAccess(long topicId) { 673 em.fireEvent(CoreEvent.CHECK_TOPIC_WRITE_ACCESS, topicId); 674 } 675 676 /** 677 * @throws AccessControlException if the current user has no permission. 678 */ 679 void checkAssociationWriteAccess(long assocId) { 680 em.fireEvent(CoreEvent.CHECK_ASSOCIATION_WRITE_ACCESS, assocId); 681 } 682 683 684 685 // === Instantiation === 686 687 <O> List<O> instantiate(Iterable<? extends DeepaMehtaObjectModelImpl> models) { 688 List<O> objects = new ArrayList(); 689 for (DeepaMehtaObjectModelImpl model : models) { 690 objects.add((O) model.instantiate()); 691 } 692 return objects; 693 } 694 695 696 697 // === 698 699 private List<String> getTopicTypeUris() { 700 try { 701 List<String> topicTypeUris = new ArrayList(); 702 // add meta types 703 topicTypeUris.add("dm4.core.topic_type"); 704 topicTypeUris.add("dm4.core.assoc_type"); 705 topicTypeUris.add("dm4.core.meta_type"); 706 // add regular types 707 for (TopicModel topicType : filterReadables(fetchTopics("type_uri", new SimpleValue( 708 "dm4.core.topic_type")))) { 709 topicTypeUris.add(topicType.getUri()); 710 } 711 return topicTypeUris; 712 } catch (Exception e) { 713 throw new RuntimeException("Fetching list of topic type URIs failed", e); 714 } 715 } 716 717 private List<String> getAssociationTypeUris() { 718 try { 719 List<String> assocTypeUris = new ArrayList(); 720 for (TopicModel assocType : filterReadables(fetchTopics("type_uri", new SimpleValue( 721 "dm4.core.assoc_type")))) { 722 assocTypeUris.add(assocType.getUri()); 723 } 724 return assocTypeUris; 725 } catch (Exception e) { 726 throw new RuntimeException("Fetching list of association type URIs failed", e); 727 } 728 } 729 730 // --- 731 732 private void createType(TypeModelImpl model, String uriPrefix) { 733 // Note: the type topic is instantiated explicitly on a `TopicModel` (which is freshly created from the 734 // `TypeModel`). Creating the type topic from the `TypeModel` directly would fail as topic creation implies 735 // topic instantiation, and due to the polymorphic `instantiate()` method a `Type` object would be instantiated 736 // (instead a `Topic` object). But instantiating a type implies per-user type projection, that is removing the 737 // assoc defs not readable by the current user. But at the time the type topic is stored in the DB its assoc 738 // defs are not yet stored, and the readability check would fail. 739 TopicModelImpl typeTopic = mf.newTopicModel(model); 740 createTopic(typeTopic, uriPrefix); // create generic topic 741 // 742 model.id = typeTopic.id; 743 model.uri = typeTopic.uri; 744 // 745 typeStorage.storeType(model); // store type-specific parts 746 } 747 748 private String typeUri(long objectId) { 749 return (String) fetchProperty(objectId, "type_uri"); 750 } 751 752 private void bootstrapTypeCache() { 753 TopicTypeModelImpl metaMetaType = mf.newTopicTypeModel("dm4.core.meta_meta_type", "Meta Meta Type", 754 "dm4.core.text"); 755 metaMetaType.setTypeUri("dm4.core.meta_meta_meta_type"); 756 typeStorage.putInTypeCache(metaMetaType); 757 } 758}