package org.cad.interruptus.repository.zookeeper; import com.google.gson.Gson; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.zookeeper.data.Stat; import org.cad.interruptus.core.GsonSerializer; import org.cad.interruptus.entity.Configuration; import org.cad.interruptus.entity.Entity; public class ConfigurationManager { final Log logger = LogFactory.getLog(getClass()); final GsonSerializer<Configuration> serializer; final AtomicReference<Configuration> reference; final CuratorFramework client; final String path; public ConfigurationManager(final CuratorFramework client, final AtomicReference<Configuration> reference, final Gson gson, final String path) { this(client, reference, new GsonSerializer(Configuration.class, gson), path); } public ConfigurationManager(final CuratorFramework client, final AtomicReference<Configuration> reference, final GsonSerializer<Configuration> serializer, final String path) { this.client = client; this.reference = reference; this.serializer = serializer; this.path = path ; } protected <R> R mutex(final Callable<R> callable, final InterProcessLock mutex) throws Exception { try { mutex.acquire(); return callable.call(); } finally { mutex.release(); } } protected <R> R mutex(final Callable<R> callable) throws Exception { return mutex(callable, new InterProcessMutex(client, this.path + ".lock")); } protected void flush() throws Exception { mutex(new Callable<Boolean>() { @Override public Boolean call() throws Exception { final String json = serializer.toJson(get()); final Stat status = client.checkExists().forPath(path); if (status != null) { client.setData() .compressed() .forPath(path, json.getBytes()); return true; } client.create() .compressed() .creatingParentsIfNeeded() .forPath(path, json.getBytes()); return true; } }); } public synchronized void save(final Entity entity) throws Exception { if (entity == null) { throw new NullPointerException("Entity cannot be null"); } get().put(entity); } public synchronized void remove(final Class<? extends Entity> clazz, final String id) throws Exception { if (id == null) { throw new NullPointerException("Entity identifier cannot be null"); } get().remove(clazz, id); } public Configuration get() throws Exception { if (reference.get() == null) { return load(); } return reference.get(); } protected synchronized Configuration load() throws Exception { logger.debug("Loading configuration"); if (reference.get() != null) { return reference.get(); } if (client.checkExists().forPath(path) == null) { logger.debug("Config file not found"); reference.set(new Configuration()); return reference.get(); } final byte[] data = client.getData().forPath(path); final Configuration item = serializer.fromJson(new String(data)); if (item == null) { logger.debug("Invalid config file"); reference.set(new Configuration()); return reference.get(); } logger.debug("Storing configuration : " + item); reference.set(item); return item; } public <T> List<T> list(final Class<? extends T> clazz) throws Exception { return new ArrayList<>(map(clazz).values()); } public <T> Map<String, T> map(Class<? extends T> clazz) throws Exception { final Configuration config = get(); final Map<String, T> result = (Map<String, T>) config.mapOf((Class<? extends Entity>) clazz); return result; } }