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            throw new IllegalStateException(String.format(
063                    "Another cache has already been registered for the given name %s", name));
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            throw new IllegalStateException("No such cache registered" + name);
084        }
085        try {
086            cache.stop();
087        } catch (RuntimeException e) {
088            log.error(String.format("Error while removing cache '%s'", name), e);
089        }
090        log.info("cache removed: " + name);
091    }
092
093    @Override
094    public CacheDescriptor clone(CacheDescriptor orig) {
095        return orig.clone();
096    }
097
098    @Override
099    public void merge(CacheDescriptor src, CacheDescriptor dst) {
100        boolean remove = src.remove;
101        // keep old remove info: if old contribution was removed, new one
102        // should replace the old one completely
103        if (remove) {
104            dst.remove = remove;
105            // don't bother merging
106            return;
107        }
108
109    }
110
111    public CacheAttributesChecker getCache(String name) {
112        if (caches.containsKey(name)) {
113            return caches.get(name).cacheChecker;
114        }
115        return null;
116    }
117
118    public List<CacheAttributesChecker> getCaches() {
119        List<CacheAttributesChecker> res = new ArrayList<CacheAttributesChecker>(caches.size());
120        for (CacheDescriptor desc : caches.values()) {
121            res.add(desc.cacheChecker);
122        }
123        return res;
124    }
125
126    public void start() {
127        RuntimeException errors = new RuntimeException("Cannot start caches, check suppressed error");
128        for (CacheDescriptor desc : caches.values()) {
129            try {
130                desc.start();
131            } catch (RuntimeException cause) {
132                errors.addSuppressed(cause);
133            }
134        }
135        if (errors.getSuppressed().length > 0) {
136            throw errors;
137        }
138        started = true;
139    }
140
141    public void stop() {
142        RuntimeException errors = new RuntimeException("Cannot stop caches, check suppressed error");
143        for (CacheDescriptor desc : caches.values()) {
144            try {
145                desc.stop();
146            } catch (RuntimeException cause) {
147                errors.addSuppressed(cause);
148            }
149        }
150        if (errors.getSuppressed().length > 0) {
151            throw errors;
152        }
153        started = false;
154    }
155
156}