package org.zenoss.zep.index.impl; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.springframework.scheduling.TaskScheduler; import org.zenoss.zep.Messages; import org.zenoss.zep.UUIDGenerator; import org.zenoss.zep.ZepException; import org.zenoss.zep.dao.EventSummaryBaseDao; import org.zenoss.zep.index.WorkQueueBuilder; import org.zenoss.zep.utils.KeyValueStore; import java.io.IOException; import java.util.*; public class DynamicConfigurableMultiBackendEventIndexDao extends MultiBackendEventIndexDao { private final KeyValueStore configStore; private volatile boolean closed = false; public DynamicConfigurableMultiBackendEventIndexDao(String name, EventSummaryBaseDao eventDao, WorkQueueBuilder queueBuilder, KeyValueStore stateStore, KeyValueStore configStore, Messages messages, TaskScheduler scheduler, UUIDGenerator uuidGenerator) { super(name, eventDao, queueBuilder, stateStore, messages, scheduler, uuidGenerator); this.configStore = configStore; } public void init() throws ZepException { super.init(); if (!useRedis) { return; } updateRedis(); Thread thread = new Thread() { @Override public void run() { logger.info(getName() + " - started"); while (!closed) { try { // Sleep for a second try { Thread.sleep(1000); } catch (InterruptedException e) { /* ignore */ } syncBackendsFromRedis(); } catch (ZepException e) { logger.error(getName() + " - error while updating configuration from Redis: " + e.getMessage(), e); } } logger.info(getName() + " - stopped"); } }; thread.setName("Multi-backend event indexer configuration monitoring thread (" + DynamicConfigurableMultiBackendEventIndexDao.this.getName() + ")"); thread.setDaemon(true); thread.start(); } private void updateRedis() throws ZepException { try { logger.info("copying backend configuration of " + getName() + " to Redis"); configStore.checkAndSetAll(new Function<Map<byte[], byte[]>, Map<byte[], byte[]>>() { @Override public Map<byte[], byte[]> apply(Map<byte[], byte[]> input) { Map<byte[], byte[]> data = Maps.newHashMap(); for (EventIndexBackendConfiguration config : getInitialBackendConfigurations()) { //overwrite any existing config in config store, //i.e. on startup always use initial config from file final byte[] key = config.getName().getBytes(); data.put(key, config.toString().getBytes()); } return data; } }); logger.debug("done copying backend configuration of " + getName() + " to Redis"); } catch (IOException e) { logger.debug("failed copying backend configuration of " + getName() + " to Redis", e); throw new ZepException(e); } catch (RuntimeException e) { logger.debug("failed copying backend configuration of " + getName() + " to Redis", e); throw e; } } private void syncBackendsFromRedis() throws ZepException { try { final List<EventIndexBackendConfiguration> backends = Lists.newArrayList(); final Set<String> forceRebuild = Sets.newHashSet(); final Set<String> toClear = Sets.newHashSet(); configStore.checkAndSetAll(new Function<Map<byte[], byte[]>, Map<byte[], byte[]>>(){ @Override public Map<byte[], byte[]> apply(Map<byte[], byte[]> input) { backends.clear(); for (byte[] keyBytes : Sets.newHashSet(input.keySet())) { final String backendId = new String(keyBytes); EventIndexBackendConfiguration current = getBackendConfiguration(backendId); final byte[] bytes = input.get(keyBytes); final String s = new String(bytes); final EventIndexBackendConfiguration c = EventIndexBackendConfiguration.parse(s); if (c == null) { logger.error("Unparsable configuration: " + s); if (current != null) input.put(keyBytes, current.toString().getBytes()); } else { if (c.getLastRebuilt() != null && c.getLastRebuilt() <= 0) { forceRebuild.add(c.getName()); c.setLastRebuilt(System.currentTimeMillis()); } if (c.getLastCleared() != null && c.getLastCleared() <= 0) { toClear.add(c.getName()); c.setLastCleared(System.currentTimeMillis()); } if (current != null) current.merge(c); else current = c; input.put(keyBytes, current.toString().getBytes()); } backends.add(current); } return input; } }); super.setBackends(backends); for (String backendId : toClear) clear(backendId); for (String backendId : forceRebuild) forceRebuild(backendId); } catch (IOException e) { throw new ZepException(e); } catch (RuntimeException e) { throw new ZepException(e); } } @Override public void close() throws IOException { super.close(); this.closed = true; } }