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.RelatedTopic;
007import de.deepamehta.core.Topic;
008import de.deepamehta.core.TopicType;
009import de.deepamehta.core.model.AssociationModel;
010import de.deepamehta.core.model.AssociationTypeModel;
011import de.deepamehta.core.model.RoleModel;
012import de.deepamehta.core.model.SimpleValue;
013import de.deepamehta.core.model.TopicModel;
014import de.deepamehta.core.model.TopicTypeModel;
015import de.deepamehta.core.service.CoreService;
016import de.deepamehta.core.service.DeepaMehtaEvent;
017import de.deepamehta.core.service.ModelFactory;
018import de.deepamehta.core.service.Plugin;
019import de.deepamehta.core.service.PluginInfo;
020import de.deepamehta.core.service.accesscontrol.AccessControl;
021import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
022
023import org.osgi.framework.BundleContext;
024
025import java.util.ArrayList;
026import java.util.List;
027import java.util.logging.Logger;
028
029
030
031/**
032 * Implementation of the DeepaMehta core service. Embeddable into Java applications.
033 */
034public class CoreServiceImpl implements CoreService {
035
036    // ---------------------------------------------------------------------------------------------- Instance Variables
037
038    BundleContext bundleContext;
039    PersistenceLayer pl;
040    EventManager em;
041    ModelFactory mf;
042    MigrationManager migrationManager;
043    PluginManager pluginManager;
044    AccessControl accessControl;
045    WebPublishingService wpService;
046
047    private Logger logger = Logger.getLogger(getClass().getName());
048
049    // ---------------------------------------------------------------------------------------------------- Constructors
050
051    /**
052     * @param   bundleContext   The context of the DeepaMehta 4 Core bundle.
053     */
054    public CoreServiceImpl(PersistenceLayer pl, BundleContext bundleContext) {
055        this.bundleContext = bundleContext;
056        this.pl = pl;
057        this.em = pl.em;
058        this.mf = pl.mf;
059        this.migrationManager = new MigrationManager(this);
060        this.pluginManager = new PluginManager(this);
061        this.accessControl = new AccessControlImpl(pl);
062        this.wpService = new WebPublishingService(pl);
063        //
064        setupDB();
065    }
066
067    // -------------------------------------------------------------------------------------------------- Public Methods
068
069
070
071    // **********************************
072    // *** CoreService Implementation ***
073    // **********************************
074
075
076
077    // === Topics ===
078
079    @Override
080    public Topic getTopic(long topicId) {
081        return pl.getTopic(topicId);
082    }
083
084    @Override
085    public TopicImpl getTopicByUri(String uri) {
086        return pl.getTopicByUri(uri);
087    }
088
089    @Override
090    public Topic getTopicByValue(String key, SimpleValue value) {
091        return pl.getTopicByValue(key, value);
092    }
093
094    @Override
095    public List<Topic> getTopicsByValue(String key, SimpleValue value) {
096        return pl.getTopicsByValue(key, value);
097    }
098
099    @Override
100    public List<Topic> getTopicsByType(String topicTypeUri) {
101        return pl.getTopicsByType(topicTypeUri);
102    }
103
104    @Override
105    public List<Topic> searchTopics(String searchTerm, String fieldUri) {
106        return pl.searchTopics(searchTerm, fieldUri);
107    }
108
109    @Override
110    public Iterable<Topic> getAllTopics() {
111        return pl.getAllTopics();
112    }
113
114    // ---
115
116    @Override
117    public TopicImpl createTopic(TopicModel model) {
118        return pl.createTopic((TopicModelImpl) model);
119    }
120
121    @Override
122    public void updateTopic(TopicModel updateModel) {
123        pl.updateTopic((TopicModelImpl) updateModel);
124    }
125
126    @Override
127    public void deleteTopic(long topicId) {
128        pl.deleteTopic(topicId);
129    }
130
131
132
133    // === Associations ===
134
135    @Override
136    public Association getAssociation(long assocId) {
137        return pl.getAssociation(assocId);
138    }
139
140    @Override
141    public Association getAssociationByValue(String key, SimpleValue value) {
142        return pl.getAssociationByValue(key, value);
143    }
144
145    @Override
146    public List<Association> getAssociationsByValue(String key, SimpleValue value) {
147        return pl.getAssociationsByValue(key, value);
148    }
149
150    @Override
151    public Association getAssociation(String assocTypeUri, long topic1Id, long topic2Id,
152                                                           String roleTypeUri1, String roleTypeUri2) {
153        return pl.getAssociation(assocTypeUri, topic1Id, topic2Id, roleTypeUri1, roleTypeUri2);
154    }
155
156    @Override
157    public Association getAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId,
158                                                                String topicRoleTypeUri, String assocRoleTypeUri) {
159        return pl.getAssociationBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, topicRoleTypeUri,
160            assocRoleTypeUri);
161    }
162
163    // ---
164
165    @Override
166    public List<Association> getAssociationsByType(String assocTypeUri) {
167        return pl.getAssociationsByType(assocTypeUri);
168    }
169
170    @Override
171    public List<Association> getAssociations(long topic1Id, long topic2Id) {
172        return pl.getAssociations(topic1Id, topic2Id);
173    }
174
175    @Override
176    public List<Association> getAssociations(long topic1Id, long topic2Id, String assocTypeUri) {
177        return pl.getAssociations(assocTypeUri, topic1Id, topic2Id);
178    }
179
180    // ---
181
182    @Override
183    public Iterable<Association> getAllAssociations() {
184        return pl.getAllAssociations();
185    }
186
187    @Override
188    public long[] getPlayerIds(long assocId) {
189        return pl.getPlayerIds(assocId);
190    }
191
192    // ---
193
194    @Override
195    public AssociationImpl createAssociation(AssociationModel model) {
196        return pl.createAssociation((AssociationModelImpl) model);
197    }
198
199    @Override
200    public void updateAssociation(AssociationModel updateModel) {
201        pl.updateAssociation((AssociationModelImpl) updateModel);
202    }
203
204    @Override
205    public void deleteAssociation(long assocId) {
206        pl.deleteAssociation(assocId);
207    }
208
209
210
211    // === Topic Types ===
212
213    @Override
214    public TopicTypeImpl getTopicType(String uri) {
215        return pl.getTopicType(uri);
216    }
217
218    @Override
219    public TopicTypeImpl getTopicTypeImplicitly(long topicId) {
220        return pl.getTopicTypeImplicitly(topicId);
221    }
222
223    // ---
224
225    @Override
226    public List<TopicType> getAllTopicTypes() {
227        return pl.getAllTopicTypes();
228    }
229
230    // ---
231
232    @Override
233    public TopicTypeImpl createTopicType(TopicTypeModel model) {
234        return pl.createTopicType((TopicTypeModelImpl) model);
235    }
236
237    @Override
238    public void updateTopicType(TopicTypeModel updateModel) {
239        pl.updateTopicType((TopicTypeModelImpl) updateModel);
240    }
241
242    @Override
243    public void deleteTopicType(String topicTypeUri) {
244        pl.deleteTopicType(topicTypeUri);
245    }
246
247
248
249    // === Association Types ===
250
251    @Override
252    public AssociationTypeImpl getAssociationType(String uri) {
253        return pl.getAssociationType(uri);
254    }
255
256    @Override
257    public AssociationTypeImpl getAssociationTypeImplicitly(long assocId) {
258        return pl.getAssociationTypeImplicitly(assocId);
259    }
260
261    // ---
262
263    @Override
264    public List<AssociationType> getAllAssociationTypes() {
265        return pl.getAllAssociationTypes();
266    }
267
268    // ---
269
270    @Override
271    public AssociationTypeImpl createAssociationType(AssociationTypeModel model) {
272        return pl.createAssociationType((AssociationTypeModelImpl) model);
273    }
274
275    @Override
276    public void updateAssociationType(AssociationTypeModel updateModel) {
277        pl.updateAssociationType((AssociationTypeModelImpl) updateModel);
278    }
279
280    @Override
281    public void deleteAssociationType(String assocTypeUri) {
282        pl.deleteAssociationType(assocTypeUri);
283    }
284
285
286
287    // === Role Types ===
288
289    @Override
290    public Topic createRoleType(TopicModel model) {
291        return pl.createRoleType((TopicModelImpl) model);
292    }
293
294
295
296    // === Generic Object ===
297
298    @Override
299    public DeepaMehtaObject getObject(long id) {
300        return pl.getObject(id);
301    }
302
303
304
305    // === Plugins ===
306
307    @Override
308    public PluginImpl getPlugin(String pluginUri) {
309        return pluginManager.getPlugin(pluginUri);
310    }
311
312    @Override
313    public List<PluginInfo> getPluginInfo() {
314        return pluginManager.getPluginInfo();
315    }
316
317
318
319    // === Events ===
320
321    @Override
322    public void fireEvent(DeepaMehtaEvent event, Object... params) {
323        em.fireEvent(event, params);
324    }
325
326    @Override
327    public void dispatchEvent(String pluginUri, DeepaMehtaEvent event, Object... params) {
328        em.dispatchEvent(getPlugin(pluginUri), event, params);
329    }
330
331
332
333    // === Properties ===
334
335    @Override
336    public Object getProperty(long id, String propUri) {
337        return pl.fetchProperty(id, propUri);
338    }
339
340    @Override
341    public boolean hasProperty(long id, String propUri) {
342        return pl.hasProperty(id, propUri);
343    }
344
345    // ---
346
347    @Override
348    public List<Topic> getTopicsByProperty(String propUri, Object propValue) {
349        return pl.getTopicsByProperty(propUri, propValue);
350    }
351
352    @Override
353    public List<Topic> getTopicsByPropertyRange(String propUri, Number from, Number to) {
354        return pl.getTopicsByPropertyRange(propUri, from, to);
355    }
356
357    @Override
358    public List<Association> getAssociationsByProperty(String propUri, Object propValue) {
359        return pl.getAssociationsByProperty(propUri, propValue);
360    }
361
362    @Override
363    public List<Association> getAssociationsByPropertyRange(String propUri, Number from, Number to) {
364        return pl.getAssociationsByPropertyRange(propUri, from, to);
365    }
366
367    // ---
368
369    @Override
370    public void addTopicPropertyIndex(String propUri) {
371        int topics = 0;
372        int added = 0;
373        logger.info("########## Adding topic property index for \"" + propUri + "\"");
374        for (Topic topic : getAllTopics()) {
375            if (topic.hasProperty(propUri)) {
376                Object value = topic.getProperty(propUri);
377                pl.indexTopicProperty(topic.getId(), propUri, value);
378                added++;
379            }
380            topics++;
381        }
382        logger.info("########## Adding topic property index complete\n    Topics processed: " + topics +
383            "\n    added to index: " + added);
384    }
385
386    @Override
387    public void addAssociationPropertyIndex(String propUri) {
388        int assocs = 0;
389        int added = 0;
390        logger.info("########## Adding association property index for \"" + propUri + "\"");
391        for (Association assoc : getAllAssociations()) {
392            if (assoc.hasProperty(propUri)) {
393                Object value = assoc.getProperty(propUri);
394                pl.indexAssociationProperty(assoc.getId(), propUri, value);
395                added++;
396            }
397            assocs++;
398        }
399        logger.info("########## Adding association property complete\n    Associations processed: " + assocs +
400            "\n    added to index: " + added);
401    }
402
403
404
405    // === Misc ===
406
407    @Override
408    public DeepaMehtaTransaction beginTx() {
409        return pl.beginTx();
410    }
411
412    // ---
413
414    @Override
415    public ModelFactory getModelFactory() {
416        return mf;
417    }
418
419    @Override
420    public AccessControl getAccessControl() {
421        return accessControl;
422    }
423
424    @Override
425    public Object getDatabaseVendorObject() {
426        return pl.getDatabaseVendorObject();
427    }
428
429    // ----------------------------------------------------------------------------------------- Package Private Methods
430
431
432
433    // === Helper ===
434
435    /**
436     * Convenience method.
437     */
438    Association createAssociation(String typeUri, RoleModel roleModel1, RoleModel roleModel2) {
439        return createAssociation(mf.newAssociationModel(typeUri, roleModel1, roleModel2));
440    }
441
442    // ------------------------------------------------------------------------------------------------- Private Methods
443
444
445
446    // === Bootstrap ===
447
448    /**
449     * Setups the database:
450     *   1) initializes the database.
451     *   2) in case of a clean install: sets up the bootstrap content.
452     *   3) runs the core migrations.
453     */
454    private void setupDB() {
455        DeepaMehtaTransaction tx = beginTx();
456        try {
457            logger.info("----- Setting up the database -----");
458            boolean isCleanInstall = pl.init();
459            if (isCleanInstall) {
460                setupBootstrapContent();
461            }
462            migrationManager.runCoreMigrations(isCleanInstall);
463            tx.success();
464            tx.finish();
465            logger.info("----- Setting up the database complete -----");
466        } catch (Exception e) {
467            logger.warning("ROLLBACK!");
468            // Note: we don't put finish() in a finally clause here because
469            // in case of error the database has to be shut down.
470            tx.finish();
471            pl.shutdown();
472            throw new RuntimeException("Setting up the database failed", e);
473        }
474    }
475
476    private void setupBootstrapContent() {
477        try {
478            // Create meta types "Topic Type" and "Association Type" -- needed to create topic types and
479            // asscociation types
480            TopicModel t = mf.newTopicModel("dm4.core.topic_type", "dm4.core.meta_type",
481                new SimpleValue("Topic Type"));
482            TopicModel a = mf.newTopicModel("dm4.core.assoc_type", "dm4.core.meta_type",
483                new SimpleValue("Association Type"));
484            _createTopic(t);
485            _createTopic(a);
486            // Create topic types "Data Type" and "Role Type"
487            // ### Note: the topic type "Data Type" depends on the data type "Text" and the data type "Text" in turn
488            // depends on the topic type "Data Type". To resolve this circle we use a low-level (storage) call here
489            // and postpone the data type association.
490            TopicModel dataType = mf.newTopicTypeModel("dm4.core.data_type", "Data Type", "dm4.core.text");
491            TopicModel roleType = mf.newTopicTypeModel("dm4.core.role_type", "Role Type", "dm4.core.text");
492            _createTopic(dataType);
493            _createTopic(roleType);
494            // Create data type "Text"
495            TopicModel text = mf.newTopicModel("dm4.core.text", "dm4.core.data_type", new SimpleValue("Text"));
496            _createTopic(text);
497            // Create role types "Default", "Type", and "Instance"
498            TopicModel deflt = mf.newTopicModel("dm4.core.default",  "dm4.core.role_type", new SimpleValue("Default"));
499            TopicModel type  = mf.newTopicModel("dm4.core.type",     "dm4.core.role_type", new SimpleValue("Type"));
500            TopicModel inst  = mf.newTopicModel("dm4.core.instance", "dm4.core.role_type", new SimpleValue("Instance"));
501            _createTopic(deflt);
502            _createTopic(type);
503            _createTopic(inst);
504            // Create association type "Aggregation" -- needed to associate topic/association types with data types
505            TopicModel aggregation = mf.newAssociationTypeModel("dm4.core.aggregation", "Aggregation", "dm4.core.text");
506            _createTopic(aggregation);
507            // Create association type "Instantiation" -- needed to associate topics with topic types
508            TopicModel instn = mf.newAssociationTypeModel("dm4.core.instantiation", "Instantiation", "dm4.core.text");
509            _createTopic(instn);
510            //
511            // 1) Postponed topic type association
512            //
513            // Note: createTopicInstantiation() creates the associations by *low-level* (storage) calls.
514            // That's why the associations can be created *before* their type (here: "dm4.core.instantiation")
515            // is fully constructed (the type's data type is not yet associated => step 2).
516            pl.createTopicInstantiation(t.getId(), t.getTypeUri());
517            pl.createTopicInstantiation(a.getId(), a.getTypeUri());
518            pl.createTopicInstantiation(dataType.getId(), dataType.getTypeUri());
519            pl.createTopicInstantiation(roleType.getId(), roleType.getTypeUri());
520            pl.createTopicInstantiation(text.getId(), text.getTypeUri());
521            pl.createTopicInstantiation(deflt.getId(), deflt.getTypeUri());
522            pl.createTopicInstantiation(type.getId(), type.getTypeUri());
523            pl.createTopicInstantiation(inst.getId(), inst.getTypeUri());
524            pl.createTopicInstantiation(aggregation.getId(), aggregation.getTypeUri());
525            pl.createTopicInstantiation(instn.getId(), instn.getTypeUri());
526            //
527            // 2) Postponed data type association
528            //
529            // Note: associateDataType() creates the association by a *high-level* (service) call.
530            // This requires the association type (here: dm4.core.aggregation) to be fully constructed already.
531            // That's why the topic type associations (step 1) must be performed *before* the data type associations.
532            // ### FIXDOC: not true anymore
533            //
534            // Note: at time of the first associateDataType() call the required association type (dm4.core.aggregation)
535            // is *not* fully constructed yet! (it gets constructed through this very call). This works anyway because
536            // the data type assigning association is created *before* the association type is fetched.
537            // (see AssociationImpl.store(): storage.storeAssociation() is called before getType()
538            // in DeepaMehtaObjectImpl.store().)
539            // ### FIXDOC: not true anymore
540            //
541            // Important is that associateDataType("dm4.core.aggregation") is the first call here.
542            // ### FIXDOC: not true anymore
543            //
544            // Note: _associateDataType() creates the data type assigning association by a *low-level* (storage) call.
545            // A high-level (service) call would fail while setting the association's value. The involved getType()
546            // would fail (not because the association is missed -- it's created meanwhile, but)
547            // because this involves fetching the association including its value. The value doesn't exist yet,
548            // because its setting forms the begin of this vicious circle.
549            _associateDataType("dm4.core.meta_type",  "dm4.core.text");
550            _associateDataType("dm4.core.topic_type", "dm4.core.text");
551            _associateDataType("dm4.core.assoc_type", "dm4.core.text");
552            _associateDataType("dm4.core.data_type",  "dm4.core.text");
553            _associateDataType("dm4.core.role_type",  "dm4.core.text");
554            //
555            _associateDataType("dm4.core.aggregation",   "dm4.core.text");
556            _associateDataType("dm4.core.instantiation", "dm4.core.text");
557        } catch (Exception e) {
558            throw new RuntimeException("Setting up the bootstrap content failed", e);
559        }
560    }
561
562    // ---
563
564    /**
565     * Low-level method that stores a topic without its "Instantiation" association.
566     * Needed for bootstrapping.
567     */
568    private void _createTopic(TopicModel model) {
569        pl.storeTopic(model);
570        pl.storeTopicValue(model.getId(), model.getSimpleValue());
571    }
572
573    /**
574     * Low-level method that stores an (data type) association without its "Instantiation" association.
575     * Needed for bootstrapping.
576     */
577    private void _associateDataType(String typeUri, String dataTypeUri) {
578        AssociationModel assoc = mf.newAssociationModel("dm4.core.aggregation",
579            mf.newTopicRoleModel(typeUri,     "dm4.core.type"),
580            mf.newTopicRoleModel(dataTypeUri, "dm4.core.default"));
581        pl.storeAssociation(assoc);
582        pl.storeAssociationValue(assoc.getId(), assoc.getSimpleValue());
583    }
584}