001    package de.deepamehta.core.impl;
002    
003    import de.deepamehta.core.Association;
004    import de.deepamehta.core.AssociationDefinition;
005    import de.deepamehta.core.AssociationType;
006    import de.deepamehta.core.DeepaMehtaObject;
007    import de.deepamehta.core.RelatedAssociation;
008    import de.deepamehta.core.RelatedTopic;
009    import de.deepamehta.core.Topic;
010    import de.deepamehta.core.TopicType;
011    import de.deepamehta.core.Type;
012    import de.deepamehta.core.model.AssociationModel;
013    import de.deepamehta.core.model.AssociationRoleModel;
014    import de.deepamehta.core.model.AssociationTypeModel;
015    import de.deepamehta.core.model.DeepaMehtaObjectModel;
016    import de.deepamehta.core.model.RelatedAssociationModel;
017    import de.deepamehta.core.model.RelatedTopicModel;
018    import de.deepamehta.core.model.RoleModel;
019    import de.deepamehta.core.model.SimpleValue;
020    import de.deepamehta.core.model.TopicModel;
021    import de.deepamehta.core.model.TopicRoleModel;
022    import de.deepamehta.core.model.TopicTypeModel;
023    import de.deepamehta.core.service.DeepaMehtaEvent;
024    import de.deepamehta.core.service.DeepaMehtaService;
025    import de.deepamehta.core.service.Plugin;
026    import de.deepamehta.core.service.PluginInfo;
027    import de.deepamehta.core.service.ResultList;
028    import de.deepamehta.core.service.TypeStorage;
029    import de.deepamehta.core.service.accesscontrol.AccessControl;
030    import de.deepamehta.core.service.accesscontrol.AccessControlException;
031    import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
032    
033    import org.osgi.framework.BundleContext;
034    
035    import java.util.ArrayList;
036    import java.util.List;
037    import java.util.logging.Logger;
038    
039    
040    
041    /**
042     * Implementation of the DeepaMehta core service. Embeddable into Java applications.
043     */
044    public class EmbeddedService implements DeepaMehtaService {
045    
046        // ------------------------------------------------------------------------------------------------------- Constants
047    
048        private static final String URI_PREFIX_TOPIC_TYPE       = "domain.project.topic_type_";
049        private static final String URI_PREFIX_ASSOCIATION_TYPE = "domain.project.assoc_type_";
050        private static final String URI_PREFIX_ROLE_TYPE        = "domain.project.role_type_";
051    
052        // ---------------------------------------------------------------------------------------------- Instance Variables
053    
054        StorageDecorator storageDecorator;
055        BundleContext bundleContext;
056        MigrationManager migrationManager;
057        PluginManager pluginManager;
058        EventManager eventManager;
059        TypeCache typeCache;
060        TypeStorageImpl typeStorage;
061        ValueStorage valueStorage;
062        AccessControl accessControl;
063    
064        private Logger logger = Logger.getLogger(getClass().getName());
065    
066        // ---------------------------------------------------------------------------------------------------- Constructors
067    
068        /**
069         * @param   bundleContext   The context of the DeepaMehta 4 Core bundle.
070         */
071        public EmbeddedService(StorageDecorator storageDecorator, BundleContext bundleContext) {
072            this.storageDecorator = storageDecorator;
073            this.bundleContext = bundleContext;
074            this.migrationManager = new MigrationManager(this);
075            this.pluginManager = new PluginManager(this);
076            this.eventManager = new EventManager(this);
077            this.typeCache = new TypeCache(this);
078            this.typeStorage = new TypeStorageImpl(this);
079            this.valueStorage = new ValueStorage(this);
080            this.accessControl = new AccessControlImpl(this);
081            //
082            bootstrapTypeCache();
083            setupDB();
084        }
085    
086        // -------------------------------------------------------------------------------------------------- Public Methods
087    
088    
089    
090        // ****************************************
091        // *** DeepaMehtaService Implementation ***
092        // ****************************************
093    
094    
095    
096        // === Topics ===
097    
098        @Override
099        public Topic getTopic(long topicId) {
100            try {
101                return instantiateTopic(storageDecorator.fetchTopic(topicId));
102            } catch (Exception e) {
103                throw new RuntimeException("Fetching topic " + topicId + " failed", e);
104            }
105        }
106    
107        @Override
108        public Topic getTopic(String key, SimpleValue value) {
109            try {
110                TopicModel topic = storageDecorator.fetchTopic(key, value);
111                return topic != null ? instantiateTopic(topic) : null;
112            } catch (Exception e) {
113                throw new RuntimeException("Fetching topic failed (key=\"" + key + "\", value=\"" + value + "\")", e);
114            }
115        }
116    
117        @Override
118        public List<Topic> getTopics(String key, SimpleValue value) {
119            try {
120                return instantiateTopics(storageDecorator.fetchTopics(key, value));
121            } catch (Exception e) {
122                throw new RuntimeException("Fetching topics failed (key=\"" + key + "\", value=\"" + value + "\")", e);
123            }
124        }
125    
126        @Override
127        public ResultList<RelatedTopic> getTopics(String topicTypeUri, int maxResultSize) {
128            try {
129                return getTopicType(topicTypeUri).getRelatedTopics("dm4.core.instantiation", "dm4.core.type",
130                    "dm4.core.instance", topicTypeUri, maxResultSize);
131            } catch (Exception e) {
132                throw new RuntimeException("Fetching topics by type failed (topicTypeUri=\"" + topicTypeUri + "\")", e);
133            }
134        }
135    
136        @Override
137        public List<Topic> searchTopics(String searchTerm, String fieldUri) {
138            try {
139                return instantiateTopics(storageDecorator.queryTopics(searchTerm, fieldUri));
140            } catch (Exception e) {
141                throw new RuntimeException("Searching topics failed (searchTerm=\"" + searchTerm + "\", fieldUri=\"" +
142                    fieldUri + "\")", e);
143            }
144        }
145    
146        @Override
147        public Iterable<Topic> getAllTopics() {
148            return new TopicIterable(this);
149        }
150    
151        // ---
152    
153        @Override
154        public Topic createTopic(TopicModel model) {
155            return createTopic(model, null);    // uriPrefix=null
156        }
157    
158        @Override
159        public void updateTopic(TopicModel model) {
160            try {
161                getTopic(model.getId()).update(model);
162            } catch (Exception e) {
163                throw new RuntimeException("Updating topic failed (" + model + ")", e);
164            }
165        }
166    
167        @Override
168        public void deleteTopic(long topicId) {
169            try {
170                getTopic(topicId).delete();
171            } catch (Exception e) {
172                throw new RuntimeException("Deleting topic " + topicId + " failed", e);
173            }
174        }
175    
176    
177    
178        // === Associations ===
179    
180        @Override
181        public Association getAssociation(long assocId) {
182            try {
183                return instantiateAssociation(storageDecorator.fetchAssociation(assocId));
184            } catch (Exception e) {
185                throw new RuntimeException("Fetching association " + assocId + " failed", e);
186            }
187        }
188    
189        @Override
190        public Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id,
191                                                               String roleTypeUri1, String roleTypeUri2) {
192            String info = "assocTypeUri=\"" + assocTypeUri + "\", topic1Id=" + topic1Id + ", topic2Id=" + topic2Id +
193                ", roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\"";
194            try {
195                AssociationModel assoc = storageDecorator.fetchAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1,
196                    roleTypeUri2);
197                return assoc != null ? instantiateAssociation(assoc) : null;
198            } catch (Exception e) {
199                throw new RuntimeException("Fetching association failed (" + info + ")", e);
200            }
201        }
202    
203        @Override
204        public Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId,
205                                                                    String topicRoleTypeUri, String assocRoleTypeUri) {
206            String info = "assocTypeUri=\"" + assocTypeUri + "\", topicId=" + topicId + ", assocId=" + assocId +
207                ", topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\"";
208            logger.info(info);
209            try {
210                AssociationModel assoc = storageDecorator.fetchAssociationBetweenTopicAndAssociation(assocTypeUri,
211                    topicId, assocId, topicRoleTypeUri, assocRoleTypeUri);
212                return assoc != null ? instantiateAssociation(assoc) : null;
213            } catch (Exception e) {
214                throw new RuntimeException("Fetching association failed (" + info + ")", e);
215            }
216        }
217    
218        // ---
219    
220        @Override
221        public ResultList<RelatedAssociation> getAssociations(String assocTypeUri) {
222            try {
223                return getAssociationType(assocTypeUri).getRelatedAssociations("dm4.core.instantiation",
224                    "dm4.core.type", "dm4.core.instance", assocTypeUri);
225            } catch (Exception e) {
226                throw new RuntimeException("Fetching associations by type failed (assocTypeUri=\"" + assocTypeUri + "\")",
227                    e);
228            }
229        }
230    
231        @Override
232        public List<Association> getAssociations(long topic1Id, long topic2Id) {
233            return getAssociations(topic1Id, topic2Id, null);
234        }
235    
236        @Override
237        public List<Association> getAssociations(long topic1Id, long topic2Id, String assocTypeUri) {
238            logger.info("topic1Id=" + topic1Id + ", topic2Id=" + topic2Id + ", assocTypeUri=\"" + assocTypeUri + "\"");
239            try {
240                return instantiateAssociations(storageDecorator.fetchAssociations(assocTypeUri, topic1Id, topic2Id,
241                    null, null));     // roleTypeUri1=null, roleTypeUri2=null
242            } catch (Exception e) {
243                throw new RuntimeException("Fetching associations between topics " + topic1Id + " and " + topic2Id +
244                    " failed (assocTypeUri=\"" + assocTypeUri + "\")", e);
245            }
246        }
247    
248        // ---
249    
250        @Override
251        public Iterable<Association> getAllAssociations() {
252            return new AssociationIterable(this);
253        }
254    
255        @Override
256        public long[] getPlayerIds(long assocId) {
257            return storageDecorator.fetchPlayerIds(assocId);
258        }
259    
260        // ---
261    
262        @Override
263        public Association createAssociation(AssociationModel model) {
264            try {
265                fireEvent(CoreEvent.PRE_CREATE_ASSOCIATION, model);
266                Association assoc = associationFactory(model);
267                fireEvent(CoreEvent.POST_CREATE_ASSOCIATION, assoc);
268                return assoc;
269            } catch (Exception e) {
270                throw new RuntimeException("Creating association failed (" + model + ")", e);
271            }
272        }
273    
274        @Override
275        public void updateAssociation(AssociationModel model) {
276            try {
277                getAssociation(model.getId()).update(model);
278            } catch (Exception e) {
279                throw new RuntimeException("Updating association failed (" + model + ")", e);
280            }
281        }
282    
283        @Override
284        public void deleteAssociation(long assocId) {
285            try {
286                getAssociation(assocId).delete();
287            } catch (Exception e) {
288                throw new RuntimeException("Deleting association " + assocId + " failed", e);
289            }
290        }
291    
292    
293    
294        // === Topic Types ===
295    
296        @Override
297        public List<String> getTopicTypeUris() {
298            try {
299                Topic metaType = instantiateTopic(storageDecorator.fetchTopic("uri",
300                    new SimpleValue("dm4.core.topic_type")));
301                ResultList<RelatedTopic> topicTypes = metaType.getRelatedTopics("dm4.core.instantiation", "dm4.core.type",
302                    "dm4.core.instance", "dm4.core.topic_type", 0);
303                List<String> topicTypeUris = new ArrayList();
304                // add meta types
305                topicTypeUris.add("dm4.core.topic_type");
306                topicTypeUris.add("dm4.core.assoc_type");
307                topicTypeUris.add("dm4.core.meta_type");
308                topicTypeUris.add("dm4.core.meta_meta_type");
309                // add regular types
310                for (Topic topicType : topicTypes) {
311                    topicTypeUris.add(topicType.getUri());
312                }
313                return topicTypeUris;
314            } catch (Exception e) {
315                throw new RuntimeException("Fetching list of topic type URIs failed", e);
316            }
317        }
318    
319        @Override
320        public TopicType getTopicType(String uri) {
321            try {
322                return typeCache.getTopicType(uri);
323            } catch (Exception e) {
324                throw new RuntimeException("Fetching topic type \"" + uri + "\" failed", e);
325            }
326        }
327    
328        @Override
329        public List<TopicType> getAllTopicTypes() {
330            try {
331                List<TopicType> topicTypes = new ArrayList();
332                for (String uri : getTopicTypeUris()) {
333                    TopicType topicType = getTopicType(uri);
334                    topicTypes.add(topicType);
335                }
336                return topicTypes;
337            } catch (Exception e) {
338                throw new RuntimeException("Fetching all topic types failed", e);
339            }
340        }
341    
342        // ---
343    
344        @Override
345        public TopicType createTopicType(TopicTypeModel model) {
346            try {
347                TopicType topicType = topicTypeFactory(model);
348                fireEvent(CoreEvent.INTRODUCE_TOPIC_TYPE, topicType);
349                return topicType;
350            } catch (Exception e) {
351                throw new RuntimeException("Creating topic type \"" + model.getUri() + "\" failed (" + model + ")", e);
352            }
353        }
354    
355        @Override
356        public void updateTopicType(TopicTypeModel model) {
357            try {
358                // Note: type lookup is by ID. The URI might have changed, the ID does not.
359                String topicTypeUri = getTopic(model.getId()).getUri();
360                getTopicType(topicTypeUri).update(model);
361            } catch (Exception e) {
362                throw new RuntimeException("Updating topic type failed (" + model + ")", e);
363            }
364        }
365    
366        @Override
367        public void deleteTopicType(String topicTypeUri) {
368            try {
369                getTopicType(topicTypeUri).delete();    // ### TODO: delete view config topics
370            } catch (Exception e) {
371                throw new RuntimeException("Deleting topic type \"" + topicTypeUri + "\" failed", e);
372            }
373        }
374    
375    
376    
377        // === Association Types ===
378    
379        @Override
380        public List<String> getAssociationTypeUris() {
381            try {
382                Topic metaType = instantiateTopic(storageDecorator.fetchTopic("uri",
383                    new SimpleValue("dm4.core.assoc_type")));
384                ResultList<RelatedTopic> assocTypes = metaType.getRelatedTopics("dm4.core.instantiation", "dm4.core.type",
385                    "dm4.core.instance", "dm4.core.assoc_type", 0);
386                List<String> assocTypeUris = new ArrayList();
387                for (Topic assocType : assocTypes) {
388                    assocTypeUris.add(assocType.getUri());
389                }
390                return assocTypeUris;
391            } catch (Exception e) {
392                throw new RuntimeException("Fetching list of association type URIs failed", e);
393            }
394        }
395    
396        @Override
397        public AssociationType getAssociationType(String uri) {
398            try {
399                return typeCache.getAssociationType(uri);
400            } catch (Exception e) {
401                throw new RuntimeException("Fetching association type \"" + uri + "\" failed", e);
402            }
403        }
404    
405        @Override
406        public List<AssociationType> getAllAssociationTypes() {
407            try {
408                List<AssociationType> assocTypes = new ArrayList();
409                for (String uri : getAssociationTypeUris()) {
410                    AssociationType assocType = getAssociationType(uri);
411                    assocTypes.add(assocType);
412                }
413                return assocTypes;
414            } catch (Exception e) {
415                throw new RuntimeException("Fetching all association types failed", e);
416            }
417        }
418    
419        // ---
420    
421        @Override
422        public AssociationType createAssociationType(AssociationTypeModel model) {
423            try {
424                AssociationType assocType = associationTypeFactory(model);
425                fireEvent(CoreEvent.INTRODUCE_ASSOCIATION_TYPE, assocType);
426                return assocType;
427            } catch (Exception e) {
428                throw new RuntimeException("Creating association type \"" + model.getUri() + "\" failed (" + model + ")",
429                    e);
430            }
431        }
432    
433        @Override
434        public void updateAssociationType(AssociationTypeModel model) {
435            try {
436                // Note: type lookup is by ID. The URI might have changed, the ID does not.
437                String assocTypeUri = getTopic(model.getId()).getUri();
438                getAssociationType(assocTypeUri).update(model);
439            } catch (Exception e) {
440                throw new RuntimeException("Updating association type failed (" + model + ")", e);
441            }
442        }
443    
444        @Override
445        public void deleteAssociationType(String assocTypeUri) {
446            try {
447                getAssociationType(assocTypeUri).delete();
448            } catch (Exception e) {
449                throw new RuntimeException("Deleting association type \"" + assocTypeUri + "\" failed", e);
450            }
451        }
452    
453    
454    
455        // === Role Types ===
456    
457        @Override
458        public Topic createRoleType(TopicModel model) {
459            // check type URI argument
460            String typeUri = model.getTypeUri();
461            if (typeUri == null) {
462                model.setTypeUri("dm4.core.role_type");
463            } else {
464                if (!typeUri.equals("dm4.core.role_type")) {
465                    throw new IllegalArgumentException("A role type is supposed to be of type \"dm4.core.role_type\" " +
466                        "(found: \"" + typeUri + "\")");
467                }
468            }
469            //
470            return createTopic(model, URI_PREFIX_ROLE_TYPE);
471        }
472    
473    
474    
475        // === Generic Object ===
476    
477        @Override
478        public DeepaMehtaObject getObject(long id) {
479            DeepaMehtaObjectModel model = storageDecorator.fetchObject(id);
480            if (model instanceof TopicModel) {
481                return instantiateTopic((TopicModel) model);
482            } else if (model instanceof AssociationModel) {
483                return instantiateAssociation((AssociationModel) model);
484            } else {
485                throw new RuntimeException("Unexpected model: " + model);
486            }
487        }
488    
489    
490    
491        // === Plugins ===
492    
493        @Override
494        public Plugin getPlugin(String pluginUri) {
495            return pluginManager.getPlugin(pluginUri);
496        }
497    
498        @Override
499        public List<PluginInfo> getPluginInfo() {
500            return pluginManager.getPluginInfo();
501        }
502    
503    
504    
505        // === Events ===
506    
507        @Override
508        public void fireEvent(DeepaMehtaEvent event, Object... params) {
509            eventManager.fireEvent(event, params);
510        }
511    
512        @Override
513        public void deliverEvent(String pluginUri, DeepaMehtaEvent event, Object... params) {
514            eventManager.deliverEvent(pluginUri, event, params);
515        }
516    
517    
518    
519        // === Properties ===
520    
521        @Override
522        public Object getProperty(long id, String propUri) {
523            return storageDecorator.fetchProperty(id, propUri);
524        }
525    
526        @Override
527        public boolean hasProperty(long id, String propUri) {
528            return storageDecorator.hasProperty(id, propUri);
529        }
530    
531        // ---
532    
533        @Override
534        public List<Topic> getTopicsByProperty(String propUri, Object propValue) {
535            return instantiateTopics(storageDecorator.fetchTopicsByProperty(propUri, propValue));
536        }
537    
538        @Override
539        public List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) {
540            return instantiateTopics(storageDecorator.fetchTopicsByPropertyRange(propUri, from, to));
541        }
542    
543        @Override
544        public List<Association> getAssociationsByProperty(String propUri, Object propValue) {
545            return instantiateAssociations(storageDecorator.fetchAssociationsByProperty(propUri, propValue));
546        }
547    
548        @Override
549        public List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) {
550            return instantiateAssociations(storageDecorator.fetchAssociationsByPropertyRange(propUri, from, to));
551        }
552    
553    
554    
555        // === Misc ===
556    
557        @Override
558        public DeepaMehtaTransaction beginTx() {
559            return storageDecorator.beginTx();
560        }
561    
562        @Override
563        public TypeStorage getTypeStorage() {
564            return typeStorage;
565        }
566    
567        @Override
568        public AccessControl getAccessControl() {
569            return accessControl;
570        }
571    
572        @Override
573        public Object getDatabaseVendorObject() {
574            return storageDecorator.getDatabaseVendorObject();
575        }
576    
577    
578    
579        // ----------------------------------------------------------------------------------------- Package Private Methods
580    
581    
582    
583        // === Helper ===
584    
585        void createTopicInstantiation(long topicId, String topicTypeUri) {
586            try {
587                AssociationModel assoc = new AssociationModel("dm4.core.instantiation",
588                    new TopicRoleModel(topicTypeUri, "dm4.core.type"),
589                    new TopicRoleModel(topicId, "dm4.core.instance"));
590                storageDecorator.storeAssociation(assoc);   // direct storage calls used here ### explain
591                storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue());
592                createAssociationInstantiation(assoc.getId(), assoc.getTypeUri());
593            } catch (Exception e) {
594                throw new RuntimeException("Associating topic " + topicId +
595                    " with topic type \"" + topicTypeUri + "\" failed", e);
596            }
597        }
598    
599        void createAssociationInstantiation(long assocId, String assocTypeUri) {
600            try {
601                AssociationModel assoc = new AssociationModel("dm4.core.instantiation",
602                    new TopicRoleModel(assocTypeUri, "dm4.core.type"),
603                    new AssociationRoleModel(assocId, "dm4.core.instance"));
604                storageDecorator.storeAssociation(assoc);   // direct storage calls used here ### explain
605                storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue());
606            } catch (Exception e) {
607                throw new RuntimeException("Associating association " + assocId +
608                    " with association type \"" + assocTypeUri + "\" failed", e);
609            }
610        }
611    
612        // ---
613    
614        /**
615         * Convenience method. ### to be dropped?
616         */
617        Association createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) {
618            return createAssociation(new AssociationModel(typeUri, roleModel1, roleModel2));
619        }
620    
621        // ------------------------------------------------------------------------------------------------- Private Methods
622    
623    
624    
625        // === Instantiation ===
626    
627        /**
628         * Attaches this core service to a topic model fetched from storage layer.
629         */
630        Topic instantiateTopic(TopicModel model) {
631            checkAccess(model);
632            return new AttachedTopic(model, this);
633        }
634    
635        private List<Topic> instantiateTopics(List<TopicModel> models) {
636            List<Topic> topics = new ArrayList();
637            for (TopicModel model : models) {
638                try {
639                    topics.add(instantiateTopic(model));
640                } catch (AccessControlException e) {
641                    // don't add topic to result and continue
642                }
643            }
644            return topics;
645        }
646    
647        // ---
648    
649        RelatedTopic instantiateRelatedTopic(RelatedTopicModel model) {
650            checkAccess(model);
651            return new AttachedRelatedTopic(model, this);
652        }
653    
654        ResultList<RelatedTopic> instantiateRelatedTopics(ResultList<RelatedTopicModel> models) {
655            List<RelatedTopic> relTopics = new ArrayList();
656            for (RelatedTopicModel model : models) {
657                try {
658                    relTopics.add(instantiateRelatedTopic(model));
659                } catch (AccessControlException e) {
660                    // don't add topic to result and continue
661                }
662            }
663            return new ResultList<RelatedTopic>(models.getTotalCount(), relTopics);
664        }
665    
666        // ===
667    
668        /**
669         * Attaches this core service to an association model fetched from storage layer.
670         */
671        Association instantiateAssociation(AssociationModel model) {
672            checkAccess(model);
673            return new AttachedAssociation(model, this);
674        }
675    
676        List<Association> instantiateAssociations(List<AssociationModel> models) {
677            List<Association> assocs = new ArrayList();
678            for (AssociationModel model : models) {
679                try {
680                    assocs.add(instantiateAssociation(model));
681                } catch (AccessControlException e) {
682                    // don't add association to result and continue
683                }
684            }
685            return assocs;
686        }
687    
688        // ---
689    
690        RelatedAssociation instantiateRelatedAssociation(RelatedAssociationModel model) {
691            checkAccess(model);
692            return new AttachedRelatedAssociation(model, this);
693        }
694    
695        ResultList<RelatedAssociation> instantiateRelatedAssociations(Iterable<RelatedAssociationModel> models) {
696            ResultList<RelatedAssociation> relAssocs = new ResultList();
697            for (RelatedAssociationModel model : models) {
698                try {
699                    relAssocs.add(instantiateRelatedAssociation(model));
700                } catch (AccessControlException e) {
701                    // don't add association to result and continue
702                }
703            }
704            return relAssocs;
705        }
706    
707    
708    
709        // === Factory ===
710    
711        private Topic createTopic(TopicModel model, String uriPrefix) {
712            try {
713                fireEvent(CoreEvent.PRE_CREATE_TOPIC, model);
714                Topic topic = topicFactory(model, uriPrefix);
715                fireEvent(CoreEvent.POST_CREATE_TOPIC, topic);
716                return topic;
717            } catch (Exception e) {
718                throw new RuntimeException("Creating topic failed (" + model + ")", e);
719            }
720        }
721    
722        // ---
723    
724        private void checkAccess(TopicModel model) {
725            fireEvent(CoreEvent.PRE_GET_TOPIC, model.getId());          // throws AccessControlException
726        }
727    
728        private void checkAccess(AssociationModel model) {
729            fireEvent(CoreEvent.PRE_GET_ASSOCIATION, model.getId());    // throws AccessControlException
730        }
731    
732        // ---
733    
734        /**
735         * Factory method: creates a new topic in the DB according to the given model
736         * and returns a topic instance.
737         */
738        private Topic topicFactory(TopicModel model, String uriPrefix) {
739            // 1) store in DB
740            storageDecorator.storeTopic(model);
741            valueStorage.storeValue(model);
742            createTopicInstantiation(model.getId(), model.getTypeUri());
743            //
744            // 2) instantiate
745            Topic topic = new AttachedTopic(model, this);
746            //
747            // 3) set default URI
748            // If no URI is given the topic gets a default URI based on its ID, if requested.
749            // Note: this must be done *after* the topic is stored. The ID is not known before.
750            // Note: in case no URI was given: once stored a topic's URI is empty (not null).
751            if (uriPrefix != null && topic.getUri().equals("")) {
752                topic.setUri(uriPrefix + topic.getId());
753            }
754            //
755            return topic;
756        }
757    
758        /**
759         * Factory method: creates a new association in the DB according to the given model
760         * and returns an association instance.
761         */
762        Association associationFactory(AssociationModel model) {
763            // 1) store in DB
764            storageDecorator.storeAssociation(model);
765            valueStorage.storeValue(model);
766            createAssociationInstantiation(model.getId(), model.getTypeUri());
767            //
768            // 2) instantiate
769            return new AttachedAssociation(model, this);
770        }
771    
772        // ---
773    
774        /**
775         * Factory method: creates a new topic type in the DB according to the given model
776         * and returns a topic type instance.
777         */
778        private TopicType topicTypeFactory(TopicTypeModel model) {
779            // 1) store in DB
780            createTopic(model, URI_PREFIX_TOPIC_TYPE);          // store generic topic
781            typeStorage.storeType(model);                       // store type-specific parts
782            //
783            // 2) instantiate
784            TopicType topicType = new AttachedTopicType(model, this);
785            typeCache.putTopicType(topicType);
786            //
787            return topicType;
788        }
789    
790        /**
791         * Factory method: creates a new association type in the DB according to the given model
792         * and returns an association type instance.
793         */
794        private AssociationType associationTypeFactory(AssociationTypeModel model) {
795            // 1) store in DB
796            createTopic(model, URI_PREFIX_ASSOCIATION_TYPE);    // store generic topic
797            typeStorage.storeType(model);                       // store type-specific parts
798            //
799            // 2) instantiate
800            AssociationType assocType = new AttachedAssociationType(model, this);
801            typeCache.putAssociationType(assocType);
802            //
803            return assocType;
804        }
805    
806    
807    
808        // === Bootstrap ===
809    
810        /**
811         * Setups the database:
812         *   1) initializes the database.
813         *   2) in case of a clean install: sets up the bootstrap content.
814         *   3) runs the core migrations.
815         */
816        private void setupDB() {
817            DeepaMehtaTransaction tx = beginTx();
818            try {
819                logger.info("----- Setting up the database -----");
820                boolean isCleanInstall = storageDecorator.init();
821                if (isCleanInstall) {
822                    setupBootstrapContent();
823                }
824                migrationManager.runCoreMigrations(isCleanInstall);
825                tx.success();
826                tx.finish();
827                logger.info("----- Setting up the database complete -----");
828            } catch (Exception e) {
829                logger.warning("ROLLBACK!");
830                // Note: we don't put finish() in a finally clause here because
831                // in case of error the database has to be shut down.
832                tx.finish();
833                storageDecorator.shutdown();
834                throw new RuntimeException("Setting up the database failed", e);
835            }
836        }
837    
838        private void setupBootstrapContent() {
839            try {
840                // Create meta types "Topic Type" and "Association Type" -- needed to create topic types and
841                // asscociation types
842                TopicModel t = new TopicModel("dm4.core.topic_type", "dm4.core.meta_type",
843                    new SimpleValue("Topic Type"));
844                TopicModel a = new TopicModel("dm4.core.assoc_type", "dm4.core.meta_type",
845                    new SimpleValue("Association Type"));
846                _createTopic(t);
847                _createTopic(a);
848                // Create topic types "Data Type" and "Role Type"
849                // ### Note: the topic type "Data Type" depends on the data type "Text" and the data type "Text" in turn
850                // depends on the topic type "Data Type". To resolve this circle we use a low-level (storage) call here
851                // and postpone the data type association.
852                TopicModel dataType = new TopicTypeModel("dm4.core.data_type", "Data Type", "dm4.core.text");
853                TopicModel roleType = new TopicTypeModel("dm4.core.role_type", "Role Type", "dm4.core.text");
854                _createTopic(dataType);
855                _createTopic(roleType);
856                // Create data type "Text"
857                TopicModel text = new TopicModel("dm4.core.text", "dm4.core.data_type", new SimpleValue("Text"));
858                _createTopic(text);
859                // Create role types "Default", "Type", and "Instance"
860                TopicModel deflt = new TopicModel("dm4.core.default",  "dm4.core.role_type", new SimpleValue("Default"));
861                TopicModel type  = new TopicModel("dm4.core.type",     "dm4.core.role_type", new SimpleValue("Type"));
862                TopicModel inst  = new TopicModel("dm4.core.instance", "dm4.core.role_type", new SimpleValue("Instance"));
863                _createTopic(deflt);
864                _createTopic(type);
865                _createTopic(inst);
866                // Create association type "Aggregation" -- needed to associate topic/association types with data types
867                TopicModel aggregation = new AssociationTypeModel("dm4.core.aggregation", "Aggregation", "dm4.core.text");
868                _createTopic(aggregation);
869                // Create association type "Instantiation" -- needed to associate topics with topic types
870                TopicModel instn = new AssociationTypeModel("dm4.core.instantiation", "Instantiation", "dm4.core.text");
871                _createTopic(instn);
872                //
873                // 1) Postponed topic type association
874                //
875                // Note: createTopicInstantiation() creates the associations by *low-level* (storage) calls.
876                // That's why the associations can be created *before* their type (here: "dm4.core.instantiation")
877                // is fully constructed (the type's data type is not yet associated => step 2).
878                createTopicInstantiation(t.getId(), t.getTypeUri());
879                createTopicInstantiation(a.getId(), a.getTypeUri());
880                createTopicInstantiation(dataType.getId(), dataType.getTypeUri());
881                createTopicInstantiation(roleType.getId(), roleType.getTypeUri());
882                createTopicInstantiation(text.getId(), text.getTypeUri());
883                createTopicInstantiation(deflt.getId(), deflt.getTypeUri());
884                createTopicInstantiation(type.getId(), type.getTypeUri());
885                createTopicInstantiation(inst.getId(), inst.getTypeUri());
886                createTopicInstantiation(aggregation.getId(), aggregation.getTypeUri());
887                createTopicInstantiation(instn.getId(), instn.getTypeUri());
888                //
889                // 2) Postponed data type association
890                //
891                // Note: associateDataType() creates the association by a *high-level* (service) call.
892                // This requires the association type (here: dm4.core.aggregation) to be fully constructed already.
893                // That's why the topic type associations (step 1) must be performed *before* the data type associations.
894                // ### FIXDOC: not true anymore
895                //
896                // Note: at time of the first associateDataType() call the required association type (dm4.core.aggregation)
897                // is *not* fully constructed yet! (it gets constructed through this very call). This works anyway because
898                // the data type assigning association is created *before* the association type is fetched.
899                // (see AttachedAssociation.store(): storage.storeAssociation() is called before getType()
900                // in AttachedDeepaMehtaObject.store().)
901                // ### FIXDOC: not true anymore
902                //
903                // Important is that associateDataType("dm4.core.aggregation") is the first call here.
904                // ### FIXDOC: not true anymore
905                //
906                // Note: _associateDataType() creates the data type assigning association by a *low-level* (storage) call.
907                // A high-level (service) call would fail while setting the association's value. The involved getType()
908                // would fail (not because the association is missed -- it's created meanwhile, but)
909                // because this involves fetching the association including its value. The value doesn't exist yet,
910                // because its setting forms the begin of this vicious circle.
911                _associateDataType("dm4.core.meta_type",  "dm4.core.text");
912                _associateDataType("dm4.core.topic_type", "dm4.core.text");
913                _associateDataType("dm4.core.assoc_type", "dm4.core.text");
914                _associateDataType("dm4.core.data_type",  "dm4.core.text");
915                _associateDataType("dm4.core.role_type",  "dm4.core.text");
916                //
917                _associateDataType("dm4.core.aggregation",   "dm4.core.text");
918                _associateDataType("dm4.core.instantiation", "dm4.core.text");
919            } catch (Exception e) {
920                throw new RuntimeException("Setting up the bootstrap content failed", e);
921            }
922        }
923    
924        // ---
925    
926        /**
927         * Low-level method that stores a topic without its "Instantiation" association.
928         * Needed for bootstrapping.
929         */
930        private void _createTopic(TopicModel model) {
931            storageDecorator.storeTopic(model);
932            storageDecorator.storeTopicValue(model.getId(), model.getSimpleValue());
933        }
934    
935        /**
936         * Low-level method that stores an (data type) association without its "Instantiation" association.
937         * Needed for bootstrapping.
938         */
939        private void _associateDataType(String typeUri, String dataTypeUri) {
940            AssociationModel assoc = new AssociationModel("dm4.core.aggregation",
941                new TopicRoleModel(typeUri,     "dm4.core.type"),
942                new TopicRoleModel(dataTypeUri, "dm4.core.default"));
943            storageDecorator.storeAssociation(assoc);
944            storageDecorator.storeAssociationValue(assoc.getId(), assoc.getSimpleValue());
945        }
946    
947        // ---
948    
949        private void bootstrapTypeCache() {
950            typeCache.putTopicType(new AttachedTopicType(new TopicTypeModel("dm4.core.meta_meta_type",
951                "dm4.core.meta_meta_meta_type", "Meta Meta Type", "dm4.core.text"), this));
952        }
953    }