/* * Copyright 2014-2016 CyberVision, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaaproject.kaa.client.configuration.base; import org.kaaproject.kaa.client.KaaClientProperties; import org.kaaproject.kaa.client.configuration.ConfigurationHashContainer; import org.kaaproject.kaa.client.configuration.ConfigurationProcessor; import org.kaaproject.kaa.client.configuration.storage.ConfigurationStorage; import org.kaaproject.kaa.client.context.ExecutorContext; import org.kaaproject.kaa.client.persistence.KaaClientState; import org.kaaproject.kaa.common.hash.EndpointObjectHash; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractConfigurationManager implements ConfigurationManager { private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigurationManager.class); protected final ConfigurationDeserializer deserializer; private final Set<ConfigurationListener> listeners = Collections.newSetFromMap( new ConcurrentHashMap<ConfigurationListener, Boolean>()); private final KaaClientProperties properties; private final ExecutorContext executorContext; private volatile byte[] configurationData; private ConfigurationStorage storage; private ConfigurationHashContainer container = new HashContainer(); private KaaClientState state; /** * All-args constructor. */ public AbstractConfigurationManager(KaaClientProperties properties, KaaClientState state, ExecutorContext executorContext) { super(); this.properties = properties; this.state = state; this.executorContext = executorContext; this.deserializer = new ConfigurationDeserializer(executorContext); } private static byte[] toByteArray(ByteBuffer buffer) { if (buffer == null) { return null; } byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); return bytes; } @Override public void init() { getConfigurationData(); } @Override public boolean addListener(ConfigurationListener listener) { if (listener != null) { LOG.trace("Adding listener {}", listener); return listeners.add(listener); } else { throw new RuntimeException("Can't add null as a listener"); } } @Override public boolean removeListener(ConfigurationListener listener) { if (listener != null) { return listeners.remove(listener); } else { throw new RuntimeException("Can't remove null listener"); } } @Override public ConfigurationProcessor getConfigurationProcessor() { return new ConfigurationProcessor() { @Override public void processConfigurationData(ByteBuffer buffer, boolean fullResync) throws IOException { if (fullResync) { configurationData = toByteArray(buffer); if (LOG.isTraceEnabled()) { LOG.trace("Received configuration data {}", Arrays.toString(configurationData)); } if (storage != null) { LOG.debug("Persisting configuration data from storage {}", storage); storage.saveConfiguration(ByteBuffer.wrap(configurationData)); LOG.debug("Persisted configuration data from storage {}", storage); } deserializer.notify(Collections.unmodifiableCollection(listeners), configurationData); } else { LOG.warn("Only full resync delta is supported!"); } } }; } @Override public void setConfigurationStorage(ConfigurationStorage storage) { this.storage = storage; } @Override public ConfigurationHashContainer getConfigurationHashContainer() { return container; } protected byte[] getConfigurationData() { if (configurationData == null) { configurationData = loadConfigurationData(); } return configurationData; } private byte[] loadConfigurationData() { if (storage != null) { if (state.isConfigurationVersionUpdated()) { LOG.info("Clearing old configuration data from storage {}", storage); try { storage.clearConfiguration(); } catch (IOException ex) { LOG.error("Failed to clear configuration from storage", ex); } } else { LOG.debug("Loading configuration data from storage {}", storage); try { configurationData = toByteArray(storage.loadConfiguration()); } catch (IOException ex) { LOG.error("Failed to load configuration from storage", ex); } } } if (configurationData == null) { LOG.debug("Loading configuration data from defaults {}", storage); configurationData = getDefaultConfigurationData(); } if (LOG.isTraceEnabled()) { LOG.trace("Loaded configuration data {}", Arrays.toString(configurationData)); } return configurationData; } protected byte[] getDefaultConfigurationData() { return properties.getDefaultConfigData(); } private class HashContainer implements ConfigurationHashContainer { @Override public EndpointObjectHash getConfigurationHash() { return EndpointObjectHash.fromSha1(getConfigurationData()); } } }