001/*
002 * (C) Copyright 2015-2019 Nuxeo (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 *     Tiry
018 *     Florent Guillaume
019 */
020package org.nuxeo.ecm.core.transientstore;
021
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Objects;
025
026import org.apache.logging.log4j.LogManager;
027import org.apache.logging.log4j.Logger;
028import org.nuxeo.ecm.core.transientstore.api.TransientStore;
029import org.nuxeo.ecm.core.transientstore.api.TransientStoreConfig;
030import org.nuxeo.ecm.core.transientstore.api.TransientStoreProvider;
031import org.nuxeo.ecm.core.transientstore.api.TransientStoreService;
032import org.nuxeo.runtime.api.Framework;
033import org.nuxeo.runtime.model.ComponentContext;
034import org.nuxeo.runtime.model.DefaultComponent;
035import org.nuxeo.runtime.model.Descriptor;
036
037/**
038 * Component exposing the {@link TransientStoreService} and managing the underlying extension point
039 *
040 * @since 7.2
041 */
042public class TransientStorageComponent extends DefaultComponent implements TransientStoreService {
043
044    private static final Logger log = LogManager.getLogger(TransientStorageComponent.class);
045
046    protected Map<String, TransientStoreProvider> stores = new HashMap<>();
047
048    public static final String EP_STORE = "store";
049
050    public static final String DEFAULT_STORE_NAME = "default";
051
052    @Override
053    public synchronized TransientStore getStore(String name) {
054        Objects.requireNonNull(name, "Transient store name cannot be null");
055        TransientStore store = stores.get(name);
056        if (store == null) {
057            TransientStoreConfig descriptor = getDescriptor(EP_STORE, name);
058            if (descriptor == null) {
059                // instantiate a copy of the default descriptor
060                descriptor = new TransientStoreConfig(getDefaultDescriptor()); // copy
061                descriptor.name = name; // set new name in copy
062            } else if (!DEFAULT_STORE_NAME.equals(name)) {
063                // make sure descriptor inherits config from default
064                descriptor = getDefaultDescriptor().merge(descriptor);
065            }
066            TransientStoreProvider provider;
067            try {
068                Class<? extends TransientStoreProvider> klass = descriptor.implClass;
069                if (klass == null) {
070                    klass = SimpleTransientStore.class;
071                }
072                provider = klass.getDeclaredConstructor().newInstance();
073                provider.init(descriptor);
074            } catch (ReflectiveOperationException e) {
075                throw new RuntimeException(e);
076            }
077            stores.put(name, provider);
078            store = provider;
079        }
080        return store;
081    }
082
083    protected TransientStoreConfig getDefaultDescriptor() {
084        TransientStoreConfig descriptor = getDescriptor(EP_STORE, DEFAULT_STORE_NAME);
085        if (descriptor == null) {
086            // TODO make this a hard error
087            String message = "Missing configuration for default transient store, using in-memory";
088            log.warn(message);
089            Framework.getRuntime().getMessageHandler().addWarning(message);
090            // use in-memory store
091            descriptor = new TransientStoreConfig(DEFAULT_STORE_NAME);
092        }
093        return descriptor;
094    }
095
096    @Override
097    public void doGC() {
098        stores.values().forEach(TransientStoreProvider::doGC);
099    }
100
101    @Override
102    protected boolean unregister(String xp, Descriptor descriptor) {
103        boolean removed = super.unregister(xp, descriptor);
104        if (removed) {
105            TransientStoreProvider store = stores.remove(descriptor.getId());
106            if (store != null) {
107                store.shutdown();
108            }
109        }
110        return removed;
111    }
112
113    @Override
114    public void start(ComponentContext context) {
115        // make sure we have a default store
116        getStore(DEFAULT_STORE_NAME);
117        // instantiate all registered stores
118        getDescriptors(EP_STORE).forEach(desc -> getStore(desc.getId()));
119    }
120
121    @Override
122    public void stop(ComponentContext context) throws InterruptedException {
123        stores.values().forEach(TransientStoreProvider::shutdown);
124        super.stop(context);
125    }
126
127    @Override
128    public void deactivate(ComponentContext context) {
129        stores.clear();
130        super.deactivate(context);
131    }
132
133    public void cleanUpStores() {
134        stores.values().forEach(TransientStoreProvider::removeAll);
135    }
136
137}