/******************************************************************************* * Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University * as Operator of the SLAC National Accelerator Laboratory. * Copyright (c) 2011 Brookhaven National Laboratory. * EPICS archiver appliance is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. *******************************************************************************/ package edu.stanford.slac.archiverappliance.PBOverHTTP; import java.io.IOException; import java.io.InputStream; import java.sql.Timestamp; import java.util.Iterator; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.common.EmptyEventIterator; import org.epics.archiverappliance.retrieval.RemotableEventStreamDesc; import org.epics.archiverappliance.retrieval.RemotableOverRaw; import org.epics.archiverappliance.retrieval.client.RetrievalEventProcessor; import edu.stanford.slac.archiverappliance.PB.EPICSEvent.PayloadInfo; import edu.stanford.slac.archiverappliance.PB.utils.LineEscaper; /** * An EventStream that is backed by an arbitrary input stream. * @author mshankar * */ public class InputStreamBackedEventStream implements EventStream, RemotableOverRaw { private static Logger logger = Logger.getLogger(InputStreamBackedEventStream.class.getName()); // We expect the PB headers to fit within this buffer size. private static int MAX_PB_HEADER_LINE_SIZE = 10*1024; private RemotableEventStreamDesc descFromFirstLine; private RetrievalEventProcessor retrievalEventProcessor; InputStream is = null; Timestamp startTime; private InputStreamBackedEventStreamIterator theIterator; public InputStreamBackedEventStream(InputStream is, Timestamp startTime) throws IOException { this.is = is; this.startTime = startTime; assert(is.markSupported()); is.mark(MAX_PB_HEADER_LINE_SIZE); try { // Read the PB descriptor from the first line but put the stream back to where it was. InputStreamBackedEventStreamIterator ret = new InputStreamBackedEventStreamIterator(this.is, startTime); byte[] line = ret.readLine(); if(line == null) return; byte[] firstLine = LineEscaper.unescapeNewLines(line); PayloadInfo info = PayloadInfo.parseFrom(firstLine); descFromFirstLine = new RemotableEventStreamDesc(info.getPvname(), info); ret.setCurrentEventStreamDesc(descFromFirstLine); logger.debug("Done reading desc from first line"); } catch(Exception ex) { logger.error("Exception parsing header", ex); throw new IOException(ex); } finally { try { is.reset(); } catch (Throwable t) { logger.error("Exception resetting mark in input stream", t); } } } public InputStreamBackedEventStream(InputStream is, Timestamp startTime, RetrievalEventProcessor retrievalEventProcessor) throws IOException { this(is, startTime); this.retrievalEventProcessor = retrievalEventProcessor; } @Override public Iterator<Event> iterator() { // The input stream has state. We could use a TeeInputStream to support multiple iterators but for now, we insist on one iterator per event stream. assert(theIterator == null); // Return an empty iterator... if(descFromFirstLine == null) return new EmptyEventIterator(); theIterator = new InputStreamBackedEventStreamIterator(this.is, startTime); if(retrievalEventProcessor != null) theIterator.setRetrievalEventProcessor(retrievalEventProcessor); try { byte[] firstLine = LineEscaper.unescapeNewLines(theIterator.readLine()); PayloadInfo info = PayloadInfo.parseFrom(firstLine); descFromFirstLine = new RemotableEventStreamDesc(info.getPvname(), info); theIterator.setCurrentEventStreamDesc(descFromFirstLine); } catch(Exception ex) { logger.error("Exception parsing header", ex); } return theIterator; } @Override public void close() { try { is.close(); } catch (Throwable t) {} } @Override public RemotableEventStreamDesc getDescription() { return descFromFirstLine; } }