edu.fullerton.ldvservlet.SrcList.java Source code

Java tutorial

Introduction

Here is the source code for edu.fullerton.ldvservlet.SrcList.java

Source

/*
 * Copyright (C) 2014 Joseph Areeda <joseph.areeda at ligo.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.fullerton.ldvservlet;

import com.areeda.jaDatabaseSupport.Database;
import edu.fullerton.jspWebUtils.Page;
import edu.fullerton.jspWebUtils.PageForm;
import edu.fullerton.jspWebUtils.PageFormButton;
import edu.fullerton.jspWebUtils.PageFormSelect;
import edu.fullerton.jspWebUtils.PageFormText;
import edu.fullerton.jspWebUtils.PageItem;
import edu.fullerton.jspWebUtils.PageItemHeader;
import edu.fullerton.jspWebUtils.PageItemImage;
import edu.fullerton.jspWebUtils.PageItemList;
import edu.fullerton.jspWebUtils.PageItemString;
import edu.fullerton.jspWebUtils.PageTable;
import edu.fullerton.jspWebUtils.PageTableColumn;
import edu.fullerton.jspWebUtils.PageTableRow;
import edu.fullerton.jspWebUtils.WebUtilException;
import edu.fullerton.ldvjutils.ChanIndexInfo;
import edu.fullerton.ldvjutils.ChanInfo;
import edu.fullerton.ldvjutils.LdvTableException;
import edu.fullerton.ldvjutils.TimeAndDate;
import edu.fullerton.ldvtables.ChanPointerTable;
import edu.fullerton.ldvtables.ChannelIndex;
import edu.fullerton.ldvtables.ChannelTable;
import edu.fullerton.ldvjutils.TimeInterval;
import edu.fullerton.ldvtables.ViewUser;
import edu.fullerton.ndsproxyclient.ChanSourceData;
import edu.fullerton.ldvw.Epochs;
import edu.fullerton.viewerplugin.PluginSupport;
import java.awt.BasicStroke;
import java.awt.Color;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.TreeSet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import viewerconfig.ViewerConfig;

/**
 * Query a list of servers and produce a list of available source frames
 * 
 * @author Joseph Areeda <joseph.areeda at ligo.org>
 */
public class SrcList extends HttpServlet {
    private ViewerConfig viewerConfig;
    private long loadTime;

    /**
     * Initialization on loading servlet, one time things like
     *
     * Load our configuration file. Make sure all the tables exist.
     *
     * @throws javax.servlet.ServletException
     */

    @Override
    public void init() throws ServletException {
        loadTime = System.currentTimeMillis();
        String[] tableNames = { "edu.fullerton.ldvtables.ErrorLog", "edu.fullerton.ldvtables.HelpTextTable",
                "edu.fullerton.ldvtables.ChannelTable", "edu.fullerton.ldvtables.ChannelIndex",
                "edu.fullerton.ldvtables.UseLog", "edu.fullerton.ldvtables.ViewUser" };
        ServletSupport servSupport = new ServletSupport();
        servSupport.checkDb(tableNames);
        viewerConfig = servSupport.getViewerConfig();
    }

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
        
    * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     * @throws edu.fullerton.jspWebUtils.WebUtilException
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException, WebUtilException {
        response.setContentType("text/html;charset=UTF-8");

        ServletSupport servletSupport;

        servletSupport = new ServletSupport();
        servletSupport.init(request, viewerConfig, false);

        Page vpage = servletSupport.getVpage();
        vpage.includeJS("showByClass.js");
        vpage.setTitle("Channel source list");
        servletSupport.addStandardHeader("");
        servletSupport.addNavBar();
        String contextPath = request.getContextPath();

        try {
            ChannelTable ctbl = new ChannelTable(servletSupport.getDb());
            ChannelIndex cindx = new ChannelIndex(servletSupport.getDb());
            ChanPointerTable cptrs = new ChanPointerTable(servletSupport.getDb());
            ArrayList<ChanSourceData> csdList = new ArrayList<>();

            String[] baseIds = request.getParameterValues("baseid");
            int tblNum = 1;
            PageItemList tables = new PageItemList();
            boolean zoom = request.getParameter("replot") != null;
            TimeInterval searchRange = new TimeInterval(0, 1800000000);
            String menuChoice = "";
            if (zoom) {
                searchRange = getSearchRange(request.getParameterMap());
                String[] timrng = (String[]) request.getParameterMap().get("timerange");
                if (timrng != null) {
                    menuChoice = timrng[0];
                }

            }

            if (baseIds != null) {
                List<Integer> chanList = new ArrayList<>();

                for (String baseId : baseIds) {
                    ChanIndexInfo cii;

                    if (baseId.trim().matches("^\\d+$")) {
                        int bid = Integer.parseInt(baseId);
                        cii = cindx.getInfo(bid);

                        String[] trtype = { "mean" };
                        if (cii.hasRaw()) {
                            chanList.addAll(cptrs.getChanList(bid, "raw"));
                        }
                        if (cii.hasRds()) {
                            chanList.addAll(cptrs.getChanList(bid, "rds"));
                        }
                        if (cii.hasMtrends()) {
                            chanList.addAll(cptrs.getChanList(bid, "minute-trend", trtype));
                        }
                        if (cii.hasStrends()) {
                            chanList.addAll(cptrs.getChanList(bid, "second-trend", trtype));
                        }
                        if (cii.hasStatic()) {
                            chanList.addAll(cptrs.getChanList(bid, "static"));
                        }
                        tblNum = addChanSource(tables, chanList, ctbl, tblNum, csdList, searchRange);
                        PageItem plots = makePlots(csdList, cii.getName(), servletSupport.getDb(),
                                servletSupport.getVpage(), servletSupport.getVuser(), contextPath);
                        PageItem plotForm = getImgForm(servletSupport, plots, bid, 0, zoom, searchRange,
                                menuChoice);
                        vpage.add(plotForm);
                        vpage.add(tables);

                    }
                }

            }
            String[] chanIds = request.getParameterValues("chanid");
            if (chanIds != null) {
                ChanInfo ci;

                for (String chanId : chanIds) {
                    if (chanId.trim().matches("^\\d+$")) {
                        int id = Integer.parseInt(chanId);
                        ci = ctbl.getChanInfo(id);
                        List<Integer> chanList = new ArrayList<>();
                        chanList.add(id);
                        tblNum = addChanSource(tables, chanList, ctbl, tblNum, csdList, searchRange);
                        PageItem plots = makePlots(csdList, ci.getChanName(), servletSupport.getDb(),
                                servletSupport.getVpage(), servletSupport.getVuser(), contextPath);
                        PageItem plotForm = getImgForm(servletSupport, plots, 0, id, zoom, searchRange, menuChoice);
                        vpage.add(plotForm);
                        vpage.add(tables);
                    }
                }
            }

            servletSupport.showPage(response);
        } catch (LdvTableException | SQLException ex) {
            String ermsg = String.format("Error showing channel source data. %1$s - %2$s",
                    ex.getClass().getSimpleName(), ex.getLocalizedMessage());
            vpage.add(ermsg);
            servletSupport.showPage(response);
        } finally {
            servletSupport.close();
        }

    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            processRequest(request, response);
        } catch (WebUtilException ex) {
            String ermsg = String.format("Error displaying source data %1$s - %2$s", ex.getClass().getSimpleName(),
                    ex.getLocalizedMessage());
            throw new ServletException(ermsg);
        }
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            processRequest(request, response);
        } catch (WebUtilException ex) {
            String ermsg = String.format("Error displaying source data %1$s - %2$s", ex.getClass().getSimpleName(),
                    ex.getLocalizedMessage());
            throw new ServletException(ermsg);
        }
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Get source list for a channel";
    }// </editor-fold>

    private int addChanSource(PageItemList tables, List<Integer> chanList, ChannelTable chanTbl, int tblNum,
            ArrayList<ChanSourceData> csdList, TimeInterval timeRange) throws LdvTableException, WebUtilException {
        TreeSet<ChanInfo> channels = new TreeSet<>();
        for (Integer chanId : chanList) {
            ChanInfo ci = chanTbl.getChanInfo(chanId);
            for (ChanInfo ci2 : channels) {
                boolean closeEnough = ci.getChanName().contentEquals(ci2.getChanName());
                closeEnough &= ci.getcType().contentEquals(ci2.getcType());
                closeEnough &= ci.getServer().contentEquals(ci2.getServer());
                if (closeEnough) {
                    ci = null;
                    break;
                }
            }
            if (ci != null) {
                channels.add(ci);
            }

        }

        for (ChanInfo ci : channels) {
            ChanSourceData csd = new ChanSourceData();
            csd.pullData(ci);

            csd.mergeIntervals(timeRange);
            csd.calcGraphData();
            csdList.add(csd);
            PageItem table = makeTable(csd, tblNum);
            tblNum++;
            tables.add(table);
            String errors = csd.getErrors();
            if (!errors.isEmpty()) {
                String ermsg = errors.replaceAll("\n", "<br>\n");
                tables.addBlankLines(1);
                tables.add(new PageItemString(ermsg, false));
            }
        }
        return tblNum;
    }

    private PageItem makeTable(ChanSourceData csd, int tblNum) throws WebUtilException {
        PageItemList ret;
        ret = new PageItemList();
        String cInfo = csd.getChanInfo().toString();
        ret.add(new PageItemHeader(cInfo, 3));
        ret.addBlankLines(1);

        String[] frmTypes = csd.getFrameTypes();
        String frmStr = "";
        for (String s : frmTypes) {
            frmStr += frmStr.isEmpty() ? s : (",  " + s);
        }

        PageTable tbl = new PageTable();

        TreeSet<TimeInterval> mergedIntervals = csd.getMergedIntervals();
        long minGps = Long.MAX_VALUE;
        long maxGps = Long.MIN_VALUE;
        long totData = 0;
        int nIntervals = 0;
        PageTableRow row;
        boolean odd = true;

        String[] colHdrs = { "Start Gps", "Start UTC", "Stop Gps", "Stop UTC", "len (s)", "d HH:MM:SS" };
        PageTableRow hdr = new PageTableRow(colHdrs, true);
        hdr.setRowType(PageTableRow.RowType.HEAD);
        tbl.addRow(hdr);
        for (TimeInterval ti : mergedIntervals) {
            Long strtGps = ti.getStartGps();
            Long stopGps = ti.getStopGps();

            String strtUtc = TimeAndDate.dateAsUtcString(TimeAndDate.gps2date(strtGps));
            String stopUtc = TimeAndDate.dateAsUtcString(TimeAndDate.gps2date(stopGps));
            minGps = Math.min(minGps, strtGps);
            maxGps = Math.max(maxGps, stopGps);
            long len = stopGps - strtGps;
            totData += len;
            nIntervals++;

            row = new PageTableRow();

            row.add(strtGps.toString());
            row.add(strtUtc);
            row.add(stopGps.toString());
            row.add(stopUtc);

            PageTableColumn lenCol = new PageTableColumn(String.format("%1$,d", len));
            lenCol.setAlign(PageItem.Alignment.RIGHT);
            row.add(lenCol);

            PageTableColumn hrTimeCol = new PageTableColumn(TimeAndDate.hrTime(len, true));
            hrTimeCol.setAlign(PageItem.Alignment.RIGHT);
            row.add(hrTimeCol);

            row.setClassName(odd ? "odd" : "even");
            odd = !odd;
            tbl.addRow(row);
        }
        float pct = totData > 0 ? 100.f * totData / (maxGps - minGps) : 0;
        String summary;
        if (nIntervals > 0) {
            String ftypeInfo = "Data is in frames of type: " + frmStr;
            ret.add(ftypeInfo);
            ret.addBlankLines(1);
            summary = String.format("Available from %1$s (%2$d) to " + "%3$s (%4$d), gaps: %5$d, coverage %6$.1f%%",
                    TimeAndDate.dateAsUtcString(TimeAndDate.gps2date(minGps)), minGps,
                    TimeAndDate.dateAsUtcString(TimeAndDate.gps2date(maxGps)), maxGps, nIntervals - 1, pct);
            ret.add(summary);
            ret.addBlankLines(2);
            String tblId = String.format("tbl%1$02d", tblNum);
            String btnId = String.format("btn%1$02d", tblNum);
            String jsCall = String.format("toggleShowById('%1$s','#%2$s')", tblId, btnId);
            PageFormButton optBtn = new PageFormButton("ShowHideBtn");
            optBtn.setText("Show interval table");
            optBtn.addEvent("onclick", jsCall);
            optBtn.setId(btnId);
            optBtn.setType("button");
            optBtn.setClassName("showCmd");
            ret.add(optBtn);

            tbl.setClassName("showHide");
            tbl.setId(tblId);
            ret.add(tbl);
            ret.addBlankLines(2);

        } else {
            ret.add("No source data found.");
        }
        return ret;
    }

    private PageItem makePlots(ArrayList<ChanSourceData> csdList, String name, Database db, Page vpage,
            ViewUser vuser, String contextPath) throws WebUtilException, LdvTableException {
        PageItemList ret = new PageItemList();
        CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new DateAxis("Date/Time (UTC)"));
        plot.setGap(10.0);

        String baseName = "";
        StringBuilder errors = new StringBuilder();
        int plotNum = 0;
        Color[] colors = { Color.RED, Color.BLUE, Color.MAGENTA, Color.ORANGE, Color.DARK_GRAY, Color.GREEN };
        boolean gotData = false;
        TimeInterval timeRange = null;
        for (ChanSourceData csd : csdList) {
            TimeInterval ti = csd.getTimeRange();
            if (ti != null) {
                if (timeRange == null) {
                    timeRange = ti;
                } else if (timeRange.overlaps(ti)) {
                    timeRange = timeRange.mergeIntervals(ti);
                } else if (ti.getStartGps() < timeRange.getStartGps()) {
                    timeRange.setStartGps(ti.getStartGps());
                } else if (ti.getStopGps() > timeRange.getStopGps()) {
                    timeRange.setStopGps(ti.getStopGps());
                }
            }
        }
        if (timeRange != null) {
            for (ChanSourceData csd : csdList) {
                baseName = csd.getChanInfo().getBaseName();
                TimeSeriesCollection mtds = new TimeSeriesCollection();
                String server = csd.getChanInfo().getServer().replace(".caltech.edu", "");
                String legend = String.format("Type: %1$s at %2$s", csd.getChanInfo().getcType(), server);
                TimeSeries ts;
                double[][] data = csd.getGraphData();
                if (data == null) {
                    data = new double[2][2];
                    data[0][0] = timeRange.getStartGps();
                    data[1][0] = timeRange.getStopGps();
                    data[0][1] = data[1][1] = 0;
                    errors.append("Error getting data for: ").append(legend).append("<br>");
                } else {
                    gotData = true;
                }
                for (double[] d : data) {
                    d[1] = d[1] <= 1 ? 1 : d[1];
                }
                ts = getTimeSeries(data, legend);

                mtds.addSeries(ts);
                XYAreaRenderer renderer = new XYAreaRenderer(XYAreaRenderer.AREA);

                BasicStroke str = new BasicStroke(2);
                int colorIdx = plotNum % colors.length;
                Color color = colors[colorIdx];
                NumberAxis yAxis = new NumberAxis("% Avail");
                yAxis.setRange(0, 100);
                renderer.setBaseFillPaint(color);
                renderer.setSeriesFillPaint(0, Color.WHITE);
                renderer.setBaseStroke(str);
                renderer.setUseFillPaint(true);
                XYPlot subplot = new XYPlot(mtds, null, yAxis, renderer);
                plot.add(subplot);

                plotNum++;
            }

            ChartPanel cpnl;
            JFreeChart chart;
            String gtitle = String.format("Available data for %1$s ", baseName);
            String subTitleTxt = String.format("From %1$s to %2$s",
                    TimeAndDate.gpsAsUtcString(timeRange.getStartGps()),
                    TimeAndDate.gpsAsUtcString(timeRange.getStopGps()));

            plot.setOrientation(PlotOrientation.VERTICAL);

            chart = new JFreeChart(gtitle, JFreeChart.DEFAULT_TITLE_FONT, plot, true);

            chart.addSubtitle(new TextTitle(subTitleTxt));
            cpnl = new ChartPanel(chart, false, false, false, false, false);

            PluginSupport psupport = new PluginSupport();
            psupport.setup(db, vpage, vuser);
            int imgId;
            PageItemImage img = null;
            try {
                psupport.setSize(800, 600);
                imgId = psupport.saveImageAsPNG(cpnl);
                String url = String.format("%1$s/view?act=getImg&amp;imgId=%2$d", contextPath, imgId);

                img = new PageItemImage(url, "availability", baseName);

            } catch (SQLException | IOException | NoSuchAlgorithmException ex) {
                String ermsg = String.format("Error creating or saving image: %1$s - $2$s",
                        ex.getClass().getSimpleName(), ex.getLocalizedMessage());
                errors.append(ermsg);

            }
            if (errors.length() > 0) {
                ret.add(errors.toString());
            }
            if (img != null) {
                ret.add(img);
            }
        } else {
            ret.add("No data to plot.");
        }
        return ret;
    }

    PageItem getImgForm(ServletSupport servletSupport, PageItem img, int bid, int cid, boolean zoom,
            TimeInterval timeRange, String menuChoice) throws WebUtilException {
        if (bid == 0 && cid == 0) {
            throw new WebUtilException("Srclist display without a base chan id or chan id");
        }
        PageItemList ret = new PageItemList();
        PageForm form = new PageForm();
        form.addHidden("replot", "true");
        if (bid > 0) {
            form.addHidden("baseid", Integer.toString(bid));
        }
        if (cid > 0) {
            form.addHidden("chanid", Integer.toString(cid));
        }
        form.setAction(servletSupport.getServletPath());
        form.setMethod("GET");
        form.setNoSubmit(true);

        PageTable imgTbl = new PageTable();
        PageTableRow imgRow = new PageTableRow();
        imgRow.add(img);
        PageItemList req = new PageItemList();
        req.add(new PageItemHeader("Replot a specific time/date range", 3));

        PageTable reqTbl = new PageTable();

        addReqRow(reqTbl, "Time range:", getTimeRangeSelector(menuChoice), "use list or specify start/end");
        String strtStr = "";
        String endStr = "now";
        if (zoom) {
            strtStr = TimeAndDate.gpsAsUtcString(timeRange.getStartGps());
            endStr = TimeAndDate.gpsAsUtcString(timeRange.getStopGps());
        }
        addReqRow(reqTbl, "Start time:", new PageFormText("start", strtStr), "leave blank for named range");
        addReqRow(reqTbl, "End time:", new PageFormText("end", endStr), "applies to start or named range");
        PageFormButton submit = new PageFormButton("submit", "Replot", "submit");
        submit.setType("submit");
        addReqRow(reqTbl, "", submit, "");

        reqTbl.setClassName("noborder");
        req.add(reqTbl);
        imgRow.add(req);
        imgTbl.addRow(imgRow);

        form.add(imgTbl);
        ret.add(form);

        return ret;
    }

    private void addReqRow(PageTable tbl, String label, PageItem it, String comment) throws WebUtilException {
        PageTableRow reqRow = new PageTableRow();
        reqRow.add(label);
        reqRow.add(it);
        reqRow.add(comment);
        reqRow.setClassAll("noborder");
        tbl.addRow(reqRow);

    }

    private TimeSeries getTimeSeries(double[][] data, String legend) {
        TimeSeries ts;
        ts = new TimeSeries(legend);
        SimpleTimeZone utctz = new SimpleTimeZone(0, "UTC");
        for (double[] data1 : data) {
            long gps = Math.round(data1[0]);
            long utcms = TimeAndDate.gps2utc(gps) * 1000;
            Date t = new Date(utcms);
            ts.addOrUpdate(new Millisecond(t, utctz, Locale.US), data1[1]);
        }
        return ts;
    }

    private PageFormSelect getTimeRangeSelector(String menuChoice) {
        PageFormSelect ret = new PageFormSelect("timerange");
        ret.add("day", "Previous 24 hrs.", Boolean.TRUE);
        ret.add("week", "Previous week.", Boolean.FALSE);
        ret.add("month", "Previous month.", Boolean.FALSE);
        Epochs epochs = new Epochs();
        ArrayList<String> epochNames = epochs.getEpochNames();
        for (String enam : epochNames) {
            ret.add(enam, enam, Boolean.FALSE);
        }
        if (!menuChoice.isEmpty()) {
            ret.setSelected(menuChoice);
        }
        return ret;
    }

    private TimeInterval getSearchRange(Map<String, String[]> parameterMap) {
        String[] timrng = parameterMap.get("timerange");
        long timeRange = 0;
        TimeInterval ret = null;

        if (timrng != null) {
            switch (timrng[0]) {
            case "day":
                timeRange = 24 * 3600;
                break;
            case "week":
                timeRange = 7 * 24 * 3600;
                break;
            case "month":
                timeRange = 30 * 24 * 3600;
                break;
            default:
                Epochs epochs = new Epochs();
                ret = epochs.getEpoch(timrng[0]);
            }
        }
        if (ret == null) {
            long endTime = TimeAndDate.nowAsGPS();
            String[] et = parameterMap.get("end");
            if (et != null && !et[0].trim().isEmpty() && !et[0].trim().equalsIgnoreCase("now")) {
                endTime = TimeAndDate.getGPS(et[0].trim());
            }
            long startTime;
            String[] st = parameterMap.get("start");
            if (st != null && !st[0].isEmpty()) {
                startTime = TimeAndDate.getGPS(st[0].trim());
            } else {
                startTime = endTime - timeRange;
            }
            ret = new TimeInterval(startTime, endTime);
        }
        return ret;
    }

}