com.ning.metrics.serialization.smile.SmileEnvelopeEventDeserializer Maven / Gradle / Ivy
/*
* Copyright 2010-2011 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.ning.metrics.serialization.smile;
import com.ning.metrics.serialization.event.EventDeserializer;
import com.ning.metrics.serialization.event.SmileEnvelopeEvent;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.smile.SmileFactory;
import org.codehaus.jackson.smile.SmileGenerator;
import org.codehaus.jackson.smile.SmileParser;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.LinkedList;
import java.util.List;
public class SmileEnvelopeEventDeserializer implements EventDeserializer
{
protected static final SmileFactory smileFactory = new SmileFactory();
protected static final JsonFactory jsonFactory = new JsonFactory();
static {
// yes, full 'compression' by checking for repeating names, short string values:
smileFactory.configure(SmileGenerator.Feature.CHECK_SHARED_NAMES, true);
smileFactory.configure(SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES, true);
// and for now let's not mandate header for input
smileFactory.configure(SmileParser.Feature.REQUIRE_HEADER, false);
}
private static final ObjectMapper smileObjectMapper = new ObjectMapper(smileFactory);
private static final ObjectMapper jsonObjectMapper = new ObjectMapper(jsonFactory);
private static final byte SMILE_MARKER = ':';
private final JsonParser parser;
private final ObjectMapper mapper;
private boolean hasFailed = false;
// used by hasNextEvent()
// keeps track of the first JsonToken that is NOT extracted
private JsonToken nextToken = null;
/**
* SmileEnvelopeEventDeserializer should be instantiated (using this constructor) if and only if
* you plan on using it to incrementally extract events (rather than extracting them all at once)
*
* @param in InputStream containing events
* @param plainJson whether the stream is in plain json (otherwise smile)
* @throws IOException generic I/O exception
*/
public SmileEnvelopeEventDeserializer(final InputStream in, final boolean plainJson) throws IOException
{
// TODO bug when using pushbackInputStream like extractEvents does. very strange.
if (!plainJson) {
parser = smileFactory.createJsonParser(in);
mapper = smileObjectMapper;
}
else {
parser = jsonFactory.createJsonParser(in);
mapper = jsonObjectMapper;
}
/* check that we either point to START_ARRAY or START_OBJECT; in former case
* it is assumed we have array of event objects; in latter case just a
* sequence of event objects.
*/
nextToken = parser.nextToken();
boolean inArray = (nextToken == JsonToken.START_ARRAY);
if (inArray) {
nextToken = parser.nextToken();
}
// either way, first 'real' event must be a JSON Object
if (nextToken != JsonToken.START_OBJECT) {
throw new IOException("Invalid stream: expected JsonToken.START_OBJECT (in array context? "
+inArray+"): instead encountered: "+nextToken);
}
}
public boolean hasNextEvent()
{
if (hasFailed) {
return false;
}
// don't advance nextToken if you don't have to
if (nextToken == JsonToken.END_ARRAY) {
return false;
}
if (nextToken == null) {
try {
// get next token
nextToken = parser.nextToken();
}
catch (Exception e) {
// why not set 'hasFailed' here?
return false;
}
}
// could verify that it is JsonToken.START_OBJECT?
return nextToken != JsonToken.END_ARRAY && nextToken != null;
}
/**
* Extracts the next event in the stream.
* Note: Stream must be formatted as an array of (serialized) SmileEnvelopeEvents.
*
* @return nextEvent. return null if it reaches the end of the list
* @throws IOException if there's a parsing issue
*/
public SmileEnvelopeEvent getNextEvent() throws IOException
{
if (!hasNextEvent()) {
return null;
}
try {
final JsonNode node = mapper.readValue(parser, JsonNode.class);
nextToken = null; // reset nextToken
return new SmileEnvelopeEvent(node);
}
// make sure we don't return true for hasNextEvent after this
catch (IOException e) {
hasFailed = true;
throw e;
}
}
/**
* Extracts all events in the stream
* Note: Stream must be formatted as an array of (serialized) SmileEnvelopeEvents.
*
* @param in InputStream containing events
* @return A list of SmileEnvelopeEvents
* @throws IOException generic I/O exception
*/
public static List extractEvents(final InputStream in) throws IOException
{
final PushbackInputStream pbIn = new PushbackInputStream(in);
final byte firstByte = (byte) pbIn.read();
// EOF?
if (firstByte == -1) {
return null;
}
pbIn.unread(firstByte);
SmileEnvelopeEventDeserializer deser = (firstByte == SMILE_MARKER) ?
new SmileEnvelopeEventDeserializer(pbIn, false) :
new SmileEnvelopeEventDeserializer(pbIn, true);
final List events = new LinkedList();
while (deser.hasNextEvent()) {
SmileEnvelopeEvent event = deser.getNextEvent();
if (event == null) {
// TODO: this is NOT expected, should warn
break;
}
events.add(event);
}
return events;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy