edu.stanford.slac.archiverappliance.PBOverHTTP.PBOverHTTPStoragePlugin.java Source code

Java tutorial

Introduction

Here is the source code for edu.stanford.slac.archiverappliance.PBOverHTTP.PBOverHTTPStoragePlugin.java

Source

/*******************************************************************************
 * 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.Event;
import org.epics.archiverappliance.EventStream;
import org.epics.archiverappliance.StoragePlugin;
import org.epics.archiverappliance.common.BasicContext;
import org.epics.archiverappliance.common.TimeUtils;
import org.epics.archiverappliance.config.ConfigService;
import org.epics.archiverappliance.etl.ConversionFunction;
import org.epics.archiverappliance.retrieval.CallableEventStream;
import org.epics.archiverappliance.retrieval.postprocessors.PostProcessor;
import org.epics.archiverappliance.utils.ui.URIUtils;

import edu.stanford.slac.archiverappliance.PB.EPICSEvent;

/**
 * A read-only storage plugin that gets data using the PB/http protocol from a server. 
 * @author mshankar
 *
 */
public class PBOverHTTPStoragePlugin implements StoragePlugin {
    private static Logger logger = Logger.getLogger(PBOverHTTPStoragePlugin.class.getName());
    private String accessURL = null;
    private String desc = "A event stream backed by a .raw response from a remote server.";
    private String name;
    private boolean skipExternalServers = false;

    @Override
    public List<Callable<EventStream>> getDataForPV(BasicContext context, String pvName, Timestamp startTime,
            Timestamp endTime, PostProcessor postProcessor) throws IOException {
        String getURL = accessURL + "?pv=" + pvName + "&from=" + TimeUtils.convertToISO8601String(startTime)
                + "&to=" + TimeUtils.convertToISO8601String(endTime)
                + (postProcessor != null ? "&pp=" + postProcessor.getExtension() : "")
                + (skipExternalServers ? "skipExternalServers=true" : "");
        logger.info("URL to fetch data is " + getURL);
        return getDataBehindURL(getURL, startTime, postProcessor);
    }

    public List<Callable<EventStream>> getDataForMultiPVs(BasicContext context, List<String> pvNames,
            Timestamp startTime, Timestamp endTime, PostProcessor postProcessor) throws IOException {
        String getURL = accessURL;
        for (int i = 0; i < pvNames.size(); i++)
            if (i == 0)
                getURL += "?pv=" + pvNames.get(i);
            else
                getURL += "&pv=" + pvNames.get(i);
        getURL += "&from=" + TimeUtils.convertToISO8601String(startTime) + "&to="
                + TimeUtils.convertToISO8601String(endTime)
                + (postProcessor != null ? "&pp=" + postProcessor.getExtension() : "")
                + (skipExternalServers ? "skipExternalServers=true" : "");
        logger.info("URL to fetch data is " + getURL);
        return getDataBehindURL(getURL, startTime, postProcessor);
    }

    private List<Callable<EventStream>> getDataBehindURL(String getURL, Timestamp startTime,
            PostProcessor postProcessor) {
        try {
            CloseableHttpClient httpclient = HttpClients.createDefault();
            HttpGet getMethod = new HttpGet(getURL);
            getMethod.addHeader("Connection", "close"); // https://www.nuxeo.com/blog/using-httpclient-properly-avoid-closewait-tcp-connections/
            try (CloseableHttpResponse response = httpclient.execute(getMethod)) {
                if (response.getStatusLine().getStatusCode() == 200) {
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        logger.debug("Obtained a HTTP entity of length " + entity.getContentLength());
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        entity.writeTo(bos);
                        bos.close();
                        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                        InputStreamBackedEventStream isStream = new InputStreamBackedEventStream(bis, startTime);
                        if (isStream.getDescription() != null) {
                            isStream.getDescription().setSource(this.getName());
                        } else {
                            logger.warn("No desc attached to input stream for url " + getURL);
                        }
                        return CallableEventStream.makeOneStreamCallableList(isStream, postProcessor, true);
                    } else {
                        logger.debug("Obtained empty HTTP entity from " + getURL);
                    }
                } else {
                    logger.warn("Invalid status code " + response.getStatusLine().getStatusCode()
                            + " when connecting to URL " + getURL);
                    HttpEntity entity = response.getEntity();
                    if (entity != null) {
                        ByteArrayOutputStream sbuf = new ByteArrayOutputStream();
                        entity.writeTo(sbuf);
                        logger.warn(sbuf.toString("UTF-8"));
                    }
                }
            }
        } catch (FileNotFoundException fex) {
            logger.debug("No data from remote site " + getURL);
            return null;
        } catch (Throwable t) {
            logger.warn("Exception fetching data from URL " + getURL, t);
        }
        return null;

    }

    @Override
    public boolean appendData(BasicContext context, String pvName, EventStream stream) {
        throw new RuntimeException("Append Data is not available for HTTP streams");
    }

    @Override
    public String getDescription() {
        return desc;
    }

    @Override
    public void initialize(String configURL, ConfigService configService) throws IOException {
        try {
            URI srcURI = new URI(configURL);
            HashMap<String, String> queryNVPairs = URIUtils.parseQueryString(srcURI);
            if (queryNVPairs.containsKey("rawURL")) {
                this.setAccessURL(queryNVPairs.get("rawURL"));
            } else {
                throw new IOException(
                        "Cannot initialize the pbraw plugin; this needs the URL to the engine/Raw over HTTP to be specified "
                                + configURL);
            }

            if (queryNVPairs.containsKey("name")) {
                name = queryNVPairs.get("name");
            } else {
                name = new URL(this.getAccessURL()).getHost();
                logger.debug("Using the default name of " + name + " for this plain pb engine");
            }

            if (queryNVPairs.containsKey("skipExternalServers")) {
                logger.debug(
                        "Telling the remote server to skip all data from external (potentially ChannelArchiver) servers");
                this.skipExternalServers = Boolean.parseBoolean(queryNVPairs.get("skipExternalServers"));
            }
        } catch (URISyntaxException ex) {
            throw new IOException(ex);
        }
    }

    public String getAccessURL() {
        return accessURL;
    }

    public void setAccessURL(String aURL) {
        this.accessURL = aURL;
        this.setDesc("PB over HTTP from URL " + aURL);
        loadPBclasses();
    }

    private static void loadPBclasses() {
        try {
            EPICSEvent.ScalarDouble.newBuilder().setSecondsintoyear(0).setNano(0).setVal(0).setSeverity(0)
                    .setStatus(0).build().toByteArray();
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    public void setDesc(String newDesc) {
        this.desc = newDesc;
    }

    @Override
    public Event getLastKnownEvent(BasicContext context, String pvName) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Event getFirstKnownEvent(BasicContext context, String pvName) throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void renamePV(BasicContext context, String oldName, String newName) throws IOException {
        // Nothing to do here.
    }

    @Override
    public void convert(BasicContext context, String pvName, ConversionFunction conversionFuntion)
            throws IOException {
        // Nothing to do here.
    }
}