/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.keystore.impl; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.coordinator.common.Configuration; import com.emc.storageos.coordinator.common.impl.ConfigurationImpl; import com.emc.storageos.security.SerializerUtils; import org.apache.curator.framework.recipes.locks.InterProcessLock; /** * Helper class for storing and retrieving configurations in coordinator. * This class handles the use of InterProcessLock such that all updates on * the coordinator configuration are synced accross the cluseter. Also, * InterProcessLocks are being reused according to the lock name given, * which makes it possible to acquire the same lock several times on a single * thread. */ public class CoordinatorConfigStoringHelper { private static Logger log = LoggerFactory.getLogger(CoordinatorConfigStoringHelper.class); private CoordinatorClient coordinator; private final Map<String, InterProcessLock> nameLockMap; public CoordinatorConfigStoringHelper() { nameLockMap = new HashMap<String, InterProcessLock>(); } public CoordinatorConfigStoringHelper(CoordinatorClient coordinator) { this.coordinator = coordinator; nameLockMap = new HashMap<String, InterProcessLock>(); } /** * * Creates or updates a new entry of the specified type in coordinator. Config is * in ZK global aread /config * * @param objToPersist * the object to store in coordinator * @param lockName * the name of the lock to use while storing this object * If passed as Null, lock is assumed to be already owned * @param configKInd * @param configId * @param ConfigKey * @throws Exception */ public void createOrUpdateConfig(Object objToPersist, String lockName, String configKind, String configId, String configKey) throws Exception { createOrUpdateConfig(objToPersist, lockName, null, configKind, configId, configKey); } /** * * Creates or updates a new entry of the specified type in coordinator. If siteId * is not null, the config is in zk site specific area. Otherwise in global area * * @param objToPersist * the object to store in coordinator * @param lockName * the name of the lock to use while storing this object * If passed as Null, lock is assumed to be already owned * @param configKInd * @param configId * @param ConfigKey * @throws Exception */ public void createOrUpdateConfig(Object objToPersist, String lockName, String siteId, String configKInd, String configId, String ConfigKey) throws Exception { InterProcessLock lock = acquireLock(lockName); try { if (lock != null) { Configuration config = coordinator.queryConfiguration(siteId, configKInd, configId); ConfigurationImpl configImpl = null; if (config == null) { configImpl = new ConfigurationImpl(); configImpl.setId(configId); configImpl.setKind(configKInd); log.debug("Creating new config"); } else { configImpl = (ConfigurationImpl) config; if (config.getKind() == null) { ((ConfigurationImpl) config).setKind(configKInd); } if (config.getId() == null) { ((ConfigurationImpl) config).setId(configId); } log.debug("Updating existing config"); } configImpl.setConfig(ConfigKey, SerializerUtils.serializeAsBase64EncodedString(objToPersist)); coordinator.persistServiceConfiguration(siteId, configImpl); log.debug("Updated config successfully"); } } finally { releaseLock(lock); } } /** * Reads object of the specified kind from coordinator and deserializes it. Config is * in ZK global aread /config * * @param configKind * @param configId * @param ConfigKey * @return the retrieved object or null if not found * @throws ClassNotFoundException * @throws IOException */ public <T> T readConfig(String configKind, String configId, String ConfigKey) throws IOException, ClassNotFoundException { return readConfig(null, configKind, configId, ConfigKey); } /** * Reads object of the specified kind from coordinator and deserializes it. If siteId * is not null, the config is in zk site specific area. Otherwise in global area * * @param siteId * @param configKind * @param configId * @param ConfigKey * @return the retrieved object or null if not found * @throws ClassNotFoundException * @throws IOException */ public <T> T readConfig(String siteId, String configKind, String configId, String ConfigKey) throws IOException, ClassNotFoundException { Configuration config = coordinator.queryConfiguration(siteId, configKind, configId); if (config == null || config.getConfig(ConfigKey) == null) { log.debug("Config of kind " + configKind + " and id " + configId + "not found"); return null; } String serializedConfig = config.getConfig(ConfigKey); @SuppressWarnings("unchecked") T retObj = (T) SerializerUtils.deserialize(serializedConfig); return retObj; } /** * Acquires an interprocess lock * * @param lockName * the lock to acquire * @return the acquired lock * @throws Exception * if failed to acquire the lock */ public synchronized InterProcessLock acquireLock(String lockName) throws Exception { InterProcessLock lock = nameLockMap.get(lockName); if (lock == null) { lock = coordinator.getSiteLocalLock(lockName); nameLockMap.put(lockName, lock); } lock.acquire(); log.info("Acquired the lock {}", lockName); return lock; } /** * release the specified lock * * @param lock * the lock to release */ public void releaseLock(InterProcessLock lock) { try { if (lock != null) { log.info("Releasing the lock {}", lock.toString()); lock.release(); log.info("Released the lock {}", lock.toString()); } } catch (Exception e) { log.error("Could not release lock"); } } /** * Removes the specified config from coordinator. Config is in global area * * @param lockName * the name of the lock to use while removing this object * @param configKInd * @param configId * @throws Exception */ public void removeConfig(String lockName, String configKind, String configId) throws Exception { removeConfig(lockName, null, configKind, configId); } /** * Removes the specified config from coordinator. If siteId is not null, config * is in global area. Otherwise it is in site specific area * * @param lockName * the name of the lock to use while removing this object * @param configKInd * @param configId * @throws Exception */ public void removeConfig(String lockName, String siteId, String configKInd, String configId) throws Exception { InterProcessLock lock = acquireLock(lockName); try { Configuration config = coordinator.queryConfiguration(siteId, configKInd, configId); if (config != null) { coordinator.removeServiceConfiguration(siteId, config); log.debug("removed config successfully"); } else { log.debug("config " + configId + " of kind " + configKInd + " was not removed since it could not be found"); } } finally { releaseLock(lock); } } /** * * Removes all configs with the specified kind from coordinator * * @param lockName * the name of the lock to use while removing this object * @param configKInd * @param configId * @param ConfigKey * @throws Exception */ public void removeAllConfigOfKInd(String lockName, String configKInd) throws Exception { InterProcessLock lock = acquireLock(lockName); try { List<Configuration> configs = coordinator.queryAllConfiguration(configKInd); if (!CollectionUtils.isEmpty(configs)) { for (Configuration configuration : configs) { coordinator.removeServiceConfiguration(configuration); } } else { log.debug("configs of kind " + configKInd + " were not removed since none were found"); } } finally { releaseLock(lock); } } /** * Reads all objects of the specified kind from coordinator * * @param configKind * @param configId * @return the retrieved objects list or null if not found * @throws ClassNotFoundException * @throws IOException */ public <T> Map<String, T> readAllConfigs(String configKind, String configKey) throws IOException, ClassNotFoundException { List<Configuration> configsList = coordinator.queryAllConfiguration(configKind); Map<String, T> returnedObjects = new HashMap<String, T>(); if (CollectionUtils.isEmpty(configsList)) { log.debug("No config of kind " + configKind + " found"); return returnedObjects; } for (Configuration config : configsList) { String serializedConfig = config.getConfig(configKey); if (serializedConfig != null) { @SuppressWarnings("unchecked") T deserialize = (T) SerializerUtils.deserialize(serializedConfig); returnedObjects.put(config.getId(), deserialize); } } return returnedObjects; } public String getSiteId() { return coordinator.getSiteId(); } /** * @param coordinator the coordinator to set */ public void setCoordinator(CoordinatorClient coordinator) { this.coordinator = coordinator; } }