package org.zenoss.zep.index.impl; import com.google.common.collect.Maps; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import org.python.google.common.base.Strings; import org.zenoss.zep.ZepException; import org.zenoss.zep.index.EventIndexBackend; import org.zenoss.zep.index.impl.MultiBackendEventIndexDao.BackendStatus; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class EventIndexBackendConfiguration implements Cloneable { private final String name; private EventIndexBackend backend; private volatile BackendStatus status; private volatile Boolean asyncUpdates; private volatile Boolean honorDeletes; private volatile Integer batchSize; private volatile Long lastCleared; private volatile Long lastRebuilt; public static final int DEFAULT_BATCH_SIZE = 1000; public static EventIndexBackendConfiguration createInstance(boolean enabled, String name, EventIndexBackend backend, BackendStatus status, boolean asyncUpdates, boolean honorDeletes, int batchSize) throws Exception { if (!enabled){ return null; } if (Strings.isNullOrEmpty(name)){ throw new RuntimeException("EventIndexBackendConfiguration name must be specified"); } if (backend == null){ throw new RuntimeException("EventIndexBackend must not be null"); } EventIndexBackendConfiguration result = new EventIndexBackendConfiguration(name, backend); result.setStatus(status); result.setAsyncUpdates(asyncUpdates); result.setHonorDeletes(honorDeletes); result.setBatchSize(batchSize); return result; } private EventIndexBackendConfiguration(String name, EventIndexBackend backend) { this.name = name; this.backend = backend; } /** You can only set the backend once, to a non-null value. * * @throws ZepException if the backend has already been set. */ public synchronized void setBackend(EventIndexBackend backend) throws ZepException { if (this.backend == backend) return; if (this.backend == null) this.backend = backend; else throw new ZepException("Cannot modify the backend of " + name + " after it has already been set."); } public void setStatus(BackendStatus status) { this.status = status; } public void setAsyncUpdates(boolean asyncUpdates) { this.asyncUpdates = asyncUpdates; } public void setHonorDeletes(boolean honorDeletes) { this.honorDeletes = honorDeletes; } public void setBatchSize(int batchSize) { this.batchSize = batchSize; } public void setLastRebuilt(long lastRebuilt) { this.lastRebuilt = lastRebuilt; } public void setLastCleared(long lastCleared) { this.lastCleared = lastCleared; } public String getName() { return name; } public EventIndexBackend getBackend() { return backend; } public BackendStatus getStatus() { return status == null ? BackendStatus.REGISTERED : status; } public boolean isWriter() { return status == BackendStatus.READER || status == BackendStatus.WRITER; } public boolean isAsyncUpdates() { return asyncUpdates == null || asyncUpdates; } public boolean isHonorDeletes() { return honorDeletes == null || honorDeletes; } public int getBatchSize() { return batchSize == null ? DEFAULT_BATCH_SIZE : batchSize; } public Long getLastRebuilt() { return lastRebuilt; } public Long getLastCleared() { return lastCleared; } public EventIndexBackendConfiguration clone() { try { return (EventIndexBackendConfiguration) super.clone(); } catch (CloneNotSupportedException e) { // Should be impossible. throw new RuntimeException(e); } } public String toString() { StringBuilder sb = new StringBuilder("EventIndexBackendConfiguration["); ObjectMapper mapper = new ObjectMapper(); Map<String,String> map = Maps.newHashMap(); if (name != null) map.put("name", name); if (status != null) map.put("status", status.toString()); if (asyncUpdates != null) map.put("asyncUpdates", asyncUpdates.toString()); if (honorDeletes != null) map.put("honorDeletes", honorDeletes.toString()); if (lastRebuilt != null) map.put("lastRebuilt", lastRebuilt.toString()); if (lastCleared != null) map.put("lastCleared", lastCleared.toString()); if (batchSize != null) map.put("batchSize", batchSize.toString()); try { sb.append(mapper.writeValueAsString(map)); } catch (IOException e) { throw new RuntimeException("Impossible exception: ", e); } sb.append("]"); return sb.toString(); } public static final Pattern PARSER = Pattern.compile( "\\A" + Pattern.quote("EventIndexBackendConfiguration[") + "(.*)" + Pattern.quote("]") + "\\z" ); /** Parses the output of {@link #toString()} * * @return a configuration object (with null backend), or else null if the string was unparsable. */ public static EventIndexBackendConfiguration parse(String s) { final Matcher matcher = PARSER.matcher(s); if (matcher.matches()) { Map<String,String> map; try { ObjectMapper mapper = new ObjectMapper(); map = mapper.readValue(matcher.group(1).getBytes(), new TypeReference<HashMap<String,String>>() {}); } catch (IOException e) { return null; } if (map.get("name") == null) return null; EventIndexBackendConfiguration result = new EventIndexBackendConfiguration(map.get("name"), null); if (map.get("status") != null) { try { result.status = BackendStatus.valueOf(map.get("status")); } catch (IllegalArgumentException e) { // ignore it } } if ("true".equals(map.get("asyncUpdates"))) result.asyncUpdates = true; else if ("false".equals(map.get("asyncUpdates"))) result.asyncUpdates = false; if ("true".equals(map.get("honorDeletes"))) result.honorDeletes = true; else if ("false".equals(map.get("honorDeletes"))) result.honorDeletes = false; if (map.get("lastRebuilt") != null) { try { result.lastRebuilt = Long.parseLong(map.get("lastRebuilt"), 10); } catch (RuntimeException e) { // ignore it } } if (map.get("lastCleared") != null) { try { result.lastCleared = Long.parseLong(map.get("lastCleared"),10); } catch (RuntimeException e) { // ignore it } } if (map.get("batchSize") != null) { try { result.batchSize = Integer.parseInt(map.get("batchSize"), 10); } catch (RuntimeException e) { // ignore it } } return result; } else return null; } public void merge(EventIndexBackendConfiguration that) { if (this.name == null || !this.name.equals(that.name)) throw new IllegalStateException(); if (that.status != null) this.status = that.status; if (that.asyncUpdates != null) this.asyncUpdates = that.asyncUpdates; if (that.honorDeletes != null) this.honorDeletes = that.honorDeletes; if (that.lastRebuilt != null) { if (this.lastRebuilt == null) this.lastRebuilt = that.lastRebuilt; else this.lastRebuilt = Math.max(this.lastRebuilt, that.lastRebuilt); } if (that.lastCleared != null) { if (this.lastCleared == null) this.lastCleared = that.lastCleared; else this.lastCleared = Math.max(this.lastCleared, that.lastCleared); } if (that.batchSize != null) this.batchSize = that.batchSize; } }