2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.netconf.persist.impl;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Optional;
13 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
14 import org.opendaylight.controller.config.persist.api.Persister;
15 import org.opendaylight.controller.config.persist.api.StorageAdapter;
16 import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
17 import org.opendaylight.controller.netconf.persist.impl.osgi.PropertiesProviderBaseImpl;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.ListIterator;
27 * {@link Persister} implementation that delegates persisting functionality to
28 * underlying {@link Persister} storages. Each storage has unique id, class, readonly value.
30 * Storage adapters are low level persisters that do the heavy lifting for this
31 * class. Instances of storage adapters can be injected directly via constructor
32 * or instantiated from a full name of its class provided in a properties file.
34 * Example configuration:<pre>
35 netconf.config.persister.active=2,3
36 # read startup configuration
37 netconf.config.persister.1.storageAdapterClass=org.opendaylight.controller.config.persist.storage.directory.DirectoryStorageAdapter
38 netconf.config.persister.1.properties.fileStorage=configuration/initial/
40 netconf.config.persister.2.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
41 netconf.config.persister.2.readonly=true
42 netconf.config.persister.2.properties.fileStorage=configuration/current/controller.config.1.txt
44 netconf.config.persister.3.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter
45 netconf.config.persister.3.properties.fileStorage=configuration/current/controller.config.2.txt
46 netconf.config.persister.3.properties.numberOfBackups=3
49 * During server startup {@link ConfigPersisterNotificationHandler} requests last snapshot from underlying storages.
50 * Each storage can respond by giving snapshot or absent response.
51 * The {@link #loadLastConfig()} will search for first non-absent response from storages ordered backwards as user
52 * specified (first '3', then '2').
54 * When a commit notification is received, '2' will be omitted because readonly flag is set to true, so
55 * only '3' will have a chance to persist new configuration. If readonly was false or not specified, both storage adapters
56 * would be called in order specified by 'netconf.config.persister' property.
59 public final class PersisterAggregator implements Persister {
60 private static final Logger logger = LoggerFactory.getLogger(PersisterAggregator.class);
62 public static class PersisterWithConfiguration {
64 public final Persister storage;
65 private final boolean readOnly;
67 public PersisterWithConfiguration(Persister storage, boolean readOnly) {
68 this.storage = storage;
69 this.readOnly = readOnly;
73 public String toString() {
74 return "PersisterWithConfiguration{" +
75 "storage=" + storage +
76 ", readOnly=" + readOnly +
81 private static PersisterWithConfiguration loadConfiguration(final String index, final PropertiesProviderBaseImpl propertiesProvider) {
83 String classKey = index + "." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX;
84 String storageAdapterClass = propertiesProvider.getProperty(classKey);
85 StorageAdapter storageAdapter;
86 if (storageAdapterClass == null || storageAdapterClass.equals("")) {
87 throw new IllegalStateException("No persister is defined in " +
88 propertiesProvider.getFullKeyForReporting(classKey)
89 + " property. Persister is not operational");
93 Class<?> clazz = Class.forName(storageAdapterClass);
94 boolean implementsCorrectIfc = StorageAdapter.class.isAssignableFrom(clazz);
95 if (implementsCorrectIfc == false) {
96 throw new IllegalArgumentException("Storage adapter " + clazz + " does not implement " + StorageAdapter.class);
98 storageAdapter = StorageAdapter.class.cast(clazz.newInstance());
100 boolean readOnly = false;
101 String readOnlyProperty = propertiesProvider.getProperty(index + "." + "readonly");
102 if (readOnlyProperty != null && readOnlyProperty.equals("true")) {
106 PropertiesProviderAdapterImpl innerProvider = new PropertiesProviderAdapterImpl(propertiesProvider, index);
107 Persister storage = storageAdapter.instantiate(innerProvider);
108 return new PersisterWithConfiguration(storage, readOnly);
109 } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
110 throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e);
115 * Persisters ordered by 'netconf.config.persister' property.
117 private final List<PersisterWithConfiguration> persisterWithConfigurations;
119 public PersisterAggregator(List<PersisterWithConfiguration> persisterWithConfigurations) {
120 this.persisterWithConfigurations = persisterWithConfigurations;
124 public static PersisterAggregator createFromProperties(PropertiesProviderBaseImpl propertiesProvider) {
125 List<PersisterWithConfiguration> persisterWithConfigurations = new ArrayList<>();
126 String prefixes = propertiesProvider.getProperty("active");
127 if (prefixes.isEmpty() == false) {
128 String [] keys = prefixes.split(",");
129 for (String index: keys) {
130 persisterWithConfigurations.add(PersisterAggregator.loadConfiguration(index, propertiesProvider));
133 logger.debug("Initialized persister with following adapters {}", persisterWithConfigurations);
134 return new PersisterAggregator(persisterWithConfigurations);
138 public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
139 for (PersisterWithConfiguration persisterWithConfiguration: persisterWithConfigurations){
140 if (!persisterWithConfiguration.readOnly){
141 logger.debug("Calling {}.persistConfig",persisterWithConfiguration.storage);
142 persisterWithConfiguration.storage.persistConfig(holder);
148 public Optional<ConfigSnapshotHolder> loadLastConfig() throws IOException {
149 // iterate in reverse order
150 ListIterator<PersisterWithConfiguration> li = persisterWithConfigurations.listIterator(persisterWithConfigurations.size());
151 while(li.hasPrevious()) {
152 PersisterWithConfiguration persisterWithConfiguration = li.previous();
153 Optional<ConfigSnapshotHolder> configSnapshotHolderOptional = persisterWithConfiguration.storage.loadLastConfig();
154 if (configSnapshotHolderOptional.isPresent()) {
155 return configSnapshotHolderOptional;
158 // no storage had an answer
159 return Optional.absent();
163 List<PersisterWithConfiguration> getPersisterWithConfigurations() {
164 return persisterWithConfigurations;
168 public void close() {
169 RuntimeException lastException = null;
170 for (PersisterWithConfiguration persisterWithConfiguration: persisterWithConfigurations){
172 persisterWithConfiguration.storage.close();
173 }catch(RuntimeException e) {
174 logger.error("Error while closing {}", persisterWithConfiguration.storage, e);
175 if (lastException == null){
178 lastException.addSuppressed(e);
182 if (lastException != null){
188 public String toString() {
189 return "PersisterAggregator{" +
190 "persisterWithConfigurations=" + persisterWithConfigurations +