package org.atomnuke.aggregator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.datatype.DatatypeConfigurationException;
import net.jps.jx.JsonReader;
import net.jps.jx.JsonWriter;
import net.jps.jx.JxFactory;
import net.jps.jx.jackson.JacksonJxFactory;
import org.atomnuke.aggregator.collectd.CollectdStatisticValue;
import org.atomnuke.atom.model.Category;
import org.atomnuke.atom.model.Entry;
import org.atomnuke.atom.model.Feed;
import org.atomnuke.atom.model.builder.CategoryBuilder;
import org.atomnuke.atom.model.builder.ContentBuilder;
import org.atomnuke.atom.model.builder.EntryBuilder;
import org.atomnuke.fallout.source.queue.QueueSource;
import org.atomnuke.fallout.source.queue.EntryQueueImpl;
import org.atomnuke.sink.AtomSink;
import org.atomnuke.sink.AtomSinkException;
import org.atomnuke.sink.AtomSinkResult;
import org.atomnuke.sink.SinkResult;
import org.atomnuke.source.AtomSource;
import org.atomnuke.source.AtomSourceException;
import org.atomnuke.source.result.AtomSourceResult;
import org.atomnuke.task.context.AtomTaskContext;
import org.atomnuke.lifecycle.InitializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author zinic
*/
public class AggregatorSink implements AtomSink, AtomSource {
private static final Logger LOG = LoggerFactory.getLogger(AggregatorSink.class);
private static final JxFactory JX_FACTORY;
static {
JxFactory jsonFactory = null;
try {
jsonFactory = new JacksonJxFactory();
} catch (DatatypeConfigurationException configurationException) {
LOG.error(configurationException.getMessage(), configurationException);
}
JX_FACTORY = jsonFactory;
}
private final JsonReader<CollectdStatisticValue> contentReader = JX_FACTORY.newReader(CollectdStatisticValue.class);
private final JsonWriter<CollectdStatisticValue> documentWriter = JX_FACTORY.newWriter(CollectdStatisticValue.class);
private final Map<String, Double> runningValues;
private final QueueSource queueSource;
public AggregatorSink() {
queueSource = new EntryQueueImpl();
runningValues = new HashMap<String, Double>();
}
private synchronized Double getValue(String key) {
return runningValues.get(key);
}
private synchronized void setValue(String key, Double valueToAdd) {
runningValues.put(key, valueToAdd);
}
@Override
public AtomSourceResult poll() throws AtomSourceException {
return queueSource.poll();
}
@Override
public SinkResult entry(Entry entry) throws AtomSinkException {
try {
for (Category category : entry.categories()) {
if (category.scheme().equals("collectd")) {
final CollectdStatisticValue statisticalValue = contentReader.read(new ByteArrayInputStream(entry.content().toString().getBytes()));
if (category.term().startsWith("fallout-test-n01.jpserver.net/memory/")) {
setValue(category.term(), statisticalValue.getValueAsDouble());
}
if (category.term().startsWith("fallout-test-n01.jpserver.net/cpu/")) {
setValue(category.term(), statisticalValue.getValueAsDouble());
}
}
}
queueSource.put(entry);
emitMemory();
emitCpu();
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
return AtomSinkResult.ok();
}
private synchronized void emitCpu() throws Exception {
double usage = 0, idle = 0;
for (Map.Entry<String, Double> valueSet : runningValues.entrySet()) {
if (valueSet.getKey().startsWith("fallout-test-n01.jpserver.net/cpu/")) {
if (valueSet.getKey().endsWith("user")) {
usage += valueSet.getValue();
} else {
idle += valueSet.getValue();
}
}
}
if (usage <= 0 && idle <= 0) {
return;
}
final EntryBuilder memoryEntry = new EntryBuilder();
memoryEntry.addCategory(new CategoryBuilder().setScheme("collectd").setTerm("fallout-test-n01.jpserver.net/cpu/percent_used").build());
final CollectdStatisticValue statisticalValue = new CollectdStatisticValue();
statisticalValue.setTimestamp(String.valueOf(System.currentTimeMillis()));
statisticalValue.setValue(String.valueOf(usage <= 0 ? 0 : 100 * idle / (idle + usage)));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
documentWriter.write(statisticalValue, baos);
final String content = new String(baos.toByteArray());
memoryEntry.setContent(new ContentBuilder().setType("application/json").setValue(content).build());
queueSource.put(memoryEntry.build());
}
private void emitMemory() throws Exception {
final Double memoryUsed = getValue("fallout-test-n01.jpserver.net/memory/used");
final Double memoryBuffered = getValue("fallout-test-n01.jpserver.net/memory/buffered");
final Double memoryCached = getValue("fallout-test-n01.jpserver.net/memory/cached");
final Double memoryFree = getValue("fallout-test-n01.jpserver.net/memory/free");
if (memoryUsed == null || memoryBuffered == null || memoryCached == null || memoryFree == null ) {
LOG.info("Still waiting for memory stats to collect...");
return;
}
final double totalMemory = memoryUsed + memoryBuffered + memoryCached + memoryFree;
final double totalUsed = memoryUsed + memoryBuffered + memoryCached;
final EntryBuilder memoryEntry = new EntryBuilder();
memoryEntry.addCategory(new CategoryBuilder().setScheme("collectd").setTerm("fallout-test-n01.jpserver.net/memory/percent_used").build());
final CollectdStatisticValue statisticalValue = new CollectdStatisticValue();
statisticalValue.setTimestamp(String.valueOf(System.currentTimeMillis()));
statisticalValue.setValue(String.valueOf(
memoryFree <= 0 ? 0 : 100 * totalUsed / totalMemory));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
documentWriter.write(statisticalValue, baos);
final String content = new String(baos.toByteArray());
memoryEntry.setContent(new ContentBuilder().setType("application/json").setValue(content).build());
queueSource.put(memoryEntry.build());
}
@Override
public SinkResult feedPage(Feed page) throws AtomSinkException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void init(AtomTaskContext tc) throws InitializationException {
}
@Override
public void destroy() {
}
}