001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Maxime Hilaire
018 *
019 */
020package org.nuxeo.ecm.core.cache;
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.runtime.model.ContributionFragmentRegistry;
030
031/**
032 * Registry to register cache
033 *
034 * @since 6.0
035 */
036public final class CacheRegistry extends ContributionFragmentRegistry<CacheDescriptor> {
037
038    private static final Log log = LogFactory.getLog(CacheRegistry.class);
039
040    // map of cache
041    protected final Map<String, CacheDescriptor> caches = new HashMap<String, CacheDescriptor>();
042
043    protected boolean started;
044
045    @Override
046    public String getContributionId(CacheDescriptor contrib) {
047        return contrib.name;
048    }
049
050    @Override
051    public void contributionUpdated(String id, CacheDescriptor descriptor, CacheDescriptor newOrigContrib) {
052        String name = descriptor.name;
053        if (name == null) {
054            throw new RuntimeException("The cache name must not be null!");
055        }
056        if (descriptor.remove) {
057            contributionRemoved(id, descriptor);
058            return;
059        }
060
061        if (caches.containsKey(name)) {
062            log.warn(String.format("Another cache has already been registered for the given name %s", name));
063            return;
064        }
065
066        caches.put(name, descriptor);
067        log.info("cache registered: " + name);
068        if (started) {
069            descriptor.start();
070        }
071    }
072
073    @Override
074    public boolean isSupportingMerge() {
075        return false;
076    }
077
078    @Override
079    public void contributionRemoved(String id, CacheDescriptor origContrib) {
080        String name = origContrib.name;
081        CacheDescriptor cache = caches.remove(name);
082        if (cache == null) {
083            log.warn("No such cache registered" + name);
084            return;
085        }
086        try {
087            cache.stop();
088        } catch (RuntimeException e) {
089            log.error(String.format("Error while removing cache '%s'", name), e);
090        }
091        log.info("cache removed: " + name);
092    }
093
094    @Override
095    public CacheDescriptor clone(CacheDescriptor orig) {
096        return orig.clone();
097    }
098
099    @Override
100    public void merge(CacheDescriptor src, CacheDescriptor dst) {
101        boolean remove = src.remove;
102        // keep old remove info: if old contribution was removed, new one
103        // should replace the old one completely
104        if (remove) {
105            dst.remove = remove;
106            // don't bother merging
107            return;
108        }
109
110    }
111
112    public Cache getCache(String name) {
113        if (caches.containsKey(name)) {
114            return caches.get(name).cache;
115        }
116        return null;
117    }
118
119    public List<Cache> getCaches() {
120        List<Cache> res = new ArrayList<>(caches.size());
121        for (CacheDescriptor desc : caches.values()) {
122            res.add(desc.cache);
123        }
124        return res;
125    }
126
127    /**
128     * Invalidate all caches
129     * 
130     * @since 9.1
131     */
132    public void invalidateAll() {
133        caches.values().forEach(CacheDescriptor::invalidateAll);
134    }
135
136    public void start() {
137        RuntimeException errors = new RuntimeException("Cannot start caches, check suppressed error");
138        for (CacheDescriptor desc : caches.values()) {
139            try {
140                desc.start();
141            } catch (RuntimeException cause) {
142                errors.addSuppressed(cause);
143            }
144        }
145        if (errors.getSuppressed().length > 0) {
146            throw errors;
147        }
148        started = true;
149    }
150
151    public void stop() {
152        RuntimeException errors = new RuntimeException("Cannot stop caches, check suppressed error");
153        for (CacheDescriptor desc : caches.values()) {
154            try {
155                desc.stop();
156            } catch (RuntimeException cause) {
157                errors.addSuppressed(cause);
158            }
159        }
160        if (errors.getSuppressed().length > 0) {
161            throw errors;
162        }
163        started = false;
164    }
165
166}