/******************************************************************************* * Copyright (c) 2012-2016 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.docker.client; import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonStreamParser; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PushbackReader; /** * Docker daemon sends chunked data in response. One chunk isn't always one JSON object so need to read full chunk at once to be able * restore JSON object. This reader merges (if needs) few chunks until get full JSON object that we can parse. * Parameter of this class is class where JSON message should be parsed. * * @author Alexander Garagatyi */ public class JsonMessageReader<T> { private static final Gson GSON = new Gson(); private final JsonStreamParser streamParser; private final Class<T> messageClass; private final PushbackReader reader; private boolean firstRead = true; /** * @param source source of messages in JSON format * @param messageClass class of the message object where JSON messages should be parsed. * Because of erasure of generic information in runtime in some cases * we can't get parameter class of current class. */ public JsonMessageReader(InputStream source, Class<T> messageClass) { // we need to push back only 1 char, read more further this.reader = new PushbackReader(new InputStreamReader(source), 1); this.streamParser = new JsonStreamParser(reader); this.messageClass = messageClass; } /** * Returns message parsed from JSON stream. * * @return object of class passed as parameter of constructor or null if stream is empty * @throws IOException if error occurs on reading stream */ public T next() throws IOException { // on first read we check if this stream is empty with reading of the first byte of stream // if so we do not call JsonStreamParser.hasNext() because it will throw exception // if not we return read byte to stream using PushbackInputStream if (firstRead) { int firstChar = reader.read(); if (firstChar == -1) { return null; } else { reader.unread(firstChar); firstRead = false; } } try { if (streamParser.hasNext()) { return GSON.fromJson(streamParser.next(), messageClass); } } catch (JsonIOException e) { throw new IOException(e); } catch (JsonParseException ignore) { } return null; } }