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}