001package de.deepamehta.core.impl;
002
003import de.deepamehta.core.TopicType;
004import de.deepamehta.core.AssociationType;
005import de.deepamehta.core.model.AssociationTypeModel;
006import de.deepamehta.core.model.TopicTypeModel;
007
008
009import java.util.HashMap;
010import java.util.Map;
011import java.util.logging.Logger;
012
013
014
015/**
016 * A memory-cache for type definitions: topic types and association types.
017 * <p>
018 * Types are accessed by the {@link get} methods. They are lazy-loaded from the DB.
019 * <p>
020 * This class is internally used by the {@link EmbeddedService}. The plugin developer accesses topic types via the
021 * {@link de.deepamehta.core.service.DeepaMehtaService#getTopicType} core service call.
022 */
023class TypeCache {
024
025    // ---------------------------------------------------------------------------------------------- Instance Variables
026
027    private Map<String, TopicType>       topicTypes = new HashMap();   // key: topic type URI
028    private Map<String, AssociationType> assocTypes = new HashMap();   // key: assoc type URI
029
030    private EmbeddedService dms;
031
032    private EndlessRecursionDetection endlessRecursionDetection = new EndlessRecursionDetection();
033
034    private Logger logger = Logger.getLogger(getClass().getName());
035
036    // ---------------------------------------------------------------------------------------------------- Constructors
037
038    TypeCache(EmbeddedService dms) {
039        this.dms = dms;
040    }
041
042    // ----------------------------------------------------------------------------------------- Package Private Methods
043
044    TopicType getTopicType(String topicTypeUri) {
045        TopicType topicType = topicTypes.get(topicTypeUri);
046        if (topicType == null) {
047            topicType = loadTopicType(topicTypeUri);
048            putTopicType(topicType);
049        }
050        return topicType;
051    }
052
053    AssociationType getAssociationType(String assocTypeUri) {
054        AssociationType assocType = assocTypes.get(assocTypeUri);
055        if (assocType == null) {
056            assocType = loadAssociationType(assocTypeUri);
057            putAssociationType(assocType);
058        }
059        return assocType;
060    }
061
062    // ---
063
064    void putTopicType(TopicType topicType) {
065        topicTypes.put(topicType.getUri(), topicType);
066    }
067
068    void putAssociationType(AssociationType assocType) {
069        assocTypes.put(assocType.getUri(), assocType);
070    }
071
072    // ---
073
074    void removeTopicType(String topicTypeUri) {
075        logger.info("### Removing topic type \"" + topicTypeUri + "\" from type cache");
076        if (topicTypes.remove(topicTypeUri) == null) {
077            throw new RuntimeException("Topic type \"" + topicTypeUri + "\" not found in type cache");
078        }
079        dms.typeStorage.removeFromTypeCache(topicTypeUri);
080    }
081
082    void removeAssociationType(String assocTypeUri) {
083        logger.info("### Removing association type \"" + assocTypeUri + "\" from type cache");
084        if (assocTypes.remove(assocTypeUri) == null) {
085            throw new RuntimeException("Association type \"" + assocTypeUri + "\" not found in type cache");
086        }
087        dms.typeStorage.removeFromTypeCache(assocTypeUri);
088    }
089
090    // ------------------------------------------------------------------------------------------------- Private Methods
091
092    private TopicType loadTopicType(String topicTypeUri) {
093        try {
094            logger.info("Loading topic type \"" + topicTypeUri + "\"");
095            endlessRecursionDetection.check(topicTypeUri);
096            //
097            TopicTypeModel model = dms.typeStorage.getTopicType(topicTypeUri);
098            return new AttachedTopicType(model, dms);
099        } finally {
100            endlessRecursionDetection.reset(topicTypeUri);
101        }
102    }
103
104    private AssociationType loadAssociationType(String assocTypeUri) {
105        try {
106            logger.info("Loading association type \"" + assocTypeUri + "\"");
107            endlessRecursionDetection.check(assocTypeUri);
108            //
109            AssociationTypeModel model = dms.typeStorage.getAssociationType(assocTypeUri);
110            return new AttachedAssociationType(model, dms);
111        } finally {
112            endlessRecursionDetection.reset(assocTypeUri);
113        }
114    }
115
116    // ---
117
118    private class EndlessRecursionDetection {
119
120        private Map<String, Boolean> loadInProgress = new HashMap();
121
122        private void check(String typeUri) {
123            if (loadInProgress.get(typeUri) != null) {
124                throw new RuntimeException("Endless recursion detected while loading type \"" + typeUri + "\"");
125            }
126            loadInProgress.put(typeUri, true);
127        }
128
129        private void reset(String typeUri) {
130            loadInProgress.remove(typeUri);
131        }
132    }
133}