001    package de.deepamehta.core.impl;
002    
003    import de.deepamehta.core.TopicType;
004    import de.deepamehta.core.AssociationType;
005    import de.deepamehta.core.model.AssociationTypeModel;
006    import de.deepamehta.core.model.TopicTypeModel;
007    
008    
009    import java.util.HashMap;
010    import java.util.Map;
011    import 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     */
023    class 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    }