/*
* Copyright (c) 2012 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.api.service.impl.resource.utils;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.model.Event;
/**
* A JSON event marshaler based on Jersey-Jackson API
*/
public class JSONEventMarshaller implements EventMarshaller {
final private Logger _logger = LoggerFactory.getLogger(JSONEventMarshaller.class);
private ObjectMapper _mapper = null;
/**
* internal count of events streamed from all threads.
*/
private final AtomicLong _count = new AtomicLong(0);
/**
* atomic boolean indicating whether the very first event has been streamed.
* This is important since in JSON format, the first element is streamed
* differently from all the rest elements.
*/
private final AtomicBoolean _firstWritten = new AtomicBoolean(false);
public JSONEventMarshaller() {
_mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
_mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);
_mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
_mapper.setSerializationInclusion(Inclusion.NON_NULL);
}
@Override
public void header(Writer writer) throws MarshallingExcetion {
BufferedWriter ow = ((BufferedWriter) writer);
try {
ow.write("{ \"events\": [");
} catch (IOException e) {
throw new MarshallingExcetion("JSON head Streaming failed", e);
}
}
@Override
public void marshal(Event event, Writer writer) throws MarshallingExcetion {
BufferedWriter ow = ((BufferedWriter) writer);
if (event == null) {
_logger.warn("null event dropped");
} else {
writeOneEvent(ow, event);
}
}
/**
* Stream out one event.
* Since the streaming format for the first event is slightly different from all the
* rest of events, this method uses a boolean to block event being streamed until
* the first event is streamed by a thread.
*
* @param writer
* - the output writer to stream the event.
* @param event
* - the event to be streamed.
* @throws MarshallingExcetion
* - failure during streaming.
*/
private void writeOneEvent(BufferedWriter writer, Event event) throws MarshallingExcetion {
try {
if (_count.getAndIncrement() > 0) {
while (!_firstWritten.get()) {
// wait until the thread which writes the first event is done
try {
Thread.sleep(1);
} catch (InterruptedException e) {
_logger.warn("Sleep interrupted");
}
}
writer.write("," + _mapper.writeValueAsString(event));
} else {
writer.write(_mapper.writeValueAsString(event));
_firstWritten.set(true);
}
} catch (JsonGenerationException e) {
throw new MarshallingExcetion("JSON Generation Error", e);
} catch (JsonMappingException e) {
throw new MarshallingExcetion("JSON Mapping Error", e);
} catch (IOException e) {
throw new MarshallingExcetion("JSON streaming failed: " + event.getEventId(), e);
}
}
@Override
public void tailer(Writer writer) throws MarshallingExcetion {
BufferedWriter ow = ((BufferedWriter) writer);
try {
ow.write("] }");
} catch (IOException e) {
throw new MarshallingExcetion("JSON tail Streaming failed", e);
}
_logger.info("{} JSON events streamed", _count);
}
}