/* Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Apr 18, 2008 */ package com.bigdata.counters.httpd; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.regex.Pattern; import org.apache.log4j.Level; import org.apache.log4j.Logger; import com.bigdata.btree.AbstractBTree; import com.bigdata.counters.CounterSet; import com.bigdata.counters.ICounterSetAccess; import com.bigdata.counters.PeriodEnum; import com.bigdata.counters.query.QueryUtil; import com.bigdata.counters.render.XHTMLRenderer; import com.bigdata.service.Event; import com.bigdata.service.IService; import com.bigdata.util.InnerCause; import com.bigdata.util.httpd.AbstractHTTPD; import com.bigdata.util.httpd.Config; import com.bigdata.util.httpd.NanoHTTPD; /** * An httpd server exposing a {@link CounterSet}. This may be used either for * testing the {@link CounterSetHTTPD} class or for post-mortem analysis of a * saved {@link CounterSet}. * * @see #main(String[]) * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class CounterSetHTTPDServer implements Runnable { final static protected transient Logger log = Logger.getLogger(NanoHTTPD.class); /** * Runs the httpd server. When the optional file(s) are given, they will be * read into a {@link CounterSet} on startup. This is useful for post-mortem * analysis. The usage is * * <pre> * [-p port] [-d {debug,info,warn,error,fatal}] [-filter filter]* [-regex regex]* [-events file] file(s) * </pre> * * where * <dl> * <dt>-p</dt> * <dd>The port at which httpd server will answer queries</dt> * <dt>-filter</dt> * <dd>Each instance of this argument specifies a string to be matched and * is used to generate a {@link Pattern} which will filter the counters read * from the file(s). If there are multiple instances they are combined * together by an OR.</dd> * <dt>-regex</dt> * <dd>Each instance of this argument specifies a regular expression to be * matched and is used to generate a {@link Pattern} which will filter the * counters read from the file(s). If there are multiple instances they are * combined together by an OR.</dd> * <dt>-events</dt> * <dd>An optional file of {@link Event} s logged in a tab-delimited format</dd> * <dt>-events</dt> * <dd>An optional file of {@link Event} s logged in a tab-delimited format</dd> * <dt>file(s)</dt> * <dd>XML representations of logged {@link CounterSet}s</dd> * </dl> * * @param args * The command line arguments. * * @throws IOException */ public static void main(final String[] args) throws Exception { // default port. int port = Config.BLAZEGRAPH_HTTP_PORT; // @todo args final int unitsToRetain = 1000; final PeriodEnum unit = PeriodEnum.Minutes; final CounterSet counterSet = new CounterSet(); final ICounterSetAccess access = new ICounterSetAccess() { public CounterSet getCounters() { return counterSet; } }; final DummyEventReportingService service = new DummyEventReportingService(); // any -filter arguments. final Collection<String> filter = new LinkedList<String>(); // any -regex arguments. final Collection<String> regex = new LinkedList<String>(); for (int i = 0; i < args.length; i++) { final String arg = args[i]; if (arg.startsWith("-")) { if (arg.equals("-p")) { port = Integer.parseInt(args[++i]); System.out.println("port: "+port); } else if( arg.equals("-d")) { final Level level = Level.toLevel(args[++i]); System.out.println("Setting server and service log levels: "+level); try { // set logging level on the server. CounterSetHTTPDServer.log.setLevel(level); // set logging level for the view. Logger.getLogger(XHTMLRenderer.class).setLevel(level); // set logging level on the service. Logger.getLogger(NanoHTTPD.class).setLevel(level); } catch (Throwable t) { /* * Note: The SLF4J logging bridge can cause a * NoSuchMethodException to be thrown here. * * @see https://sourceforge.net/apps/trac/bigdata/ticket/362 */ if (InnerCause.isInnerCause(t, NoSuchMethodException.class)) { log.error("Could not set log level : " + AbstractBTree.dumpLog.getName()); } else { // Some other problem. throw new RuntimeException(t); } } } else if (arg.equals("-events")) { QueryUtil.readEvents(service, new File(args[++i])); } else if( arg.equals("-filter")) { filter.add(args[++i]); } else if( arg.equals("-regex")) { regex.add(args[++i]); } else { System.err.println("Unknown option: "+arg); System.exit( 1 ); } } else { /* * Compute the optional filter to be applied when reading this * file. */ final Pattern pattern = QueryUtil.getPattern(filter, regex); /* * Read counters accepted by the optional filter into the * counter set to be served. */ QueryUtil.readCountersFromFile(new File(arg), counterSet, pattern, unitsToRetain, unit); } } System.out.println("Starting httpd server on port=" + port); // new server. final CounterSetHTTPDServer server = new CounterSetHTTPDServer(port, access, service); // run server. server.run(); } /** The server. */ private AbstractHTTPD httpd; /** * * @param port */ public CounterSetHTTPDServer(final int port, final ICounterSetAccess counterSet, final IService service) throws Exception { /* * The runtime shutdown hook appears to be a robust way to handle ^C by * providing a clean service termination. */ Runtime.getRuntime().addShutdownHook(new ShutdownThread(this)); httpd = new CounterSetHTTPD(port, counterSet, service); } public void run() { Object keepAlive = new Object(); synchronized (keepAlive) { try { keepAlive.wait(); } catch (InterruptedException ex) { if(log.isInfoEnabled()) log.info(ex); } finally { // terminate. shutdownNow(); } } } synchronized public void shutdownNow() { if(log.isInfoEnabled()) log.info("begin"); if (httpd != null) { httpd.shutdownNow(); httpd = null; } if(log.isInfoEnabled()) log.info("done"); } /** * Runs {@link #shutdownNow()}. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ static class ShutdownThread extends Thread { final CounterSetHTTPDServer server; public ShutdownThread(CounterSetHTTPDServer httpd) { if (httpd == null) throw new IllegalArgumentException(); this.server = httpd; } public void run() { try { if(log.isInfoEnabled()) log.info("Running shutdown."); /* * Note: This is the "server" shutdown. */ server.shutdownNow(); } catch (Exception ex) { log.error("While shutting down service: " + ex, ex); } } } }