![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.counters.httpd.CounterSetHTTPD Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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
*/
package com.bigdata.counters.httpd;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import com.bigdata.counters.CounterSet;
import com.bigdata.counters.ICounterSetAccess;
import com.bigdata.counters.query.CounterSetSelector;
import com.bigdata.counters.query.ICounterSelector;
import com.bigdata.counters.query.URLQueryModel;
import com.bigdata.counters.render.IRenderer;
import com.bigdata.counters.render.RendererFactory;
import com.bigdata.service.IService;
import com.bigdata.util.Bytes;
import com.bigdata.util.httpd.AbstractHTTPD;
/**
* Exposes a {@link CounterSet} via HTTPD.
*
* @author Bryan Thompson
* @version $Id$
*/
public class CounterSetHTTPD extends AbstractHTTPD {
static private final Logger log = Logger.getLogger(CounterSetHTTPD.class);
/**
* Access to the {@link CounterSet} exposed by this service.
*/
private final ICounterSetAccess accessor;
/**
* The service reference iff one one specified to the ctor (may be null).
*/
private final IService service;
/**
* The minimum time before a client can force the re-materialization of the
* {@link CounterSet}. This is designed to limit the impact of the client on
* the service.
*
* TODO Configuration parameter for {@link #minUpdateLatency}
*/
private final long minUpdateLatency = 5000;
/**
* The last materialized {@link CounterSet}.
*/
private volatile CounterSet counterSet = null;
/**
* The timestamp of the last materialized {@link CounterSet}.
*/
private volatile long lastTimestamp = 0L;
/**
* Class used to pre-declare classpath resources that are available for
* download via httpd.
*
* @author Bryan Thompson
* @version $Id$
*/
private static class DeclaredResource {
final String localResource;
final String mimeType;
/**
*
* @param localResource
* The name of a resource to be located using the classpath.
* @param mimeType
* The mime type of that resource.
*/
public DeclaredResource(final String localResource,
final String mimeType) throws IOException {
this.localResource = localResource;
this.mimeType = mimeType;
if (localResource == null)
throw new IllegalArgumentException();
if (mimeType == null)
throw new IllegalArgumentException();
// verify that the resource is on the classpath.
InputStream is = getInputStream();
is.close();
}
public InputStream getInputStream() throws IOException {
final InputStream is = getClass()
.getResourceAsStream(localResource);
if (is == null) {
throw new IOException("Resource not on classpath: "
+ localResource);
}
return is;
}
}
/**
* An immutable collection of pre-declared classpath resources which can be
* downloaded via httpd.
*/
private final Map allowedClassPathResources;
/**
* The service reference iff one one specified to the ctor (may be null).
*/
final protected IService getService() {
return service;
}
public CounterSetHTTPD(final int port, final ICounterSetAccess accessor) throws IOException {
this(port, accessor, null/*fed*/);
}
/**
*
* @param port
* @param accessor
* @param service
* Optional reference to the service within which this httpd is
* hosted.
* @throws IOException
*/
public CounterSetHTTPD(final int port, final ICounterSetAccess accessor,
final IService service) throws IOException {
super(port);
if(accessor == null)
throw new IllegalArgumentException();
this.accessor = accessor;
// Note: MAY be null.
this.service = service;
final HashMap map = new HashMap();
/*
* Pre-declare resources that will be served from the CLASSPATH.
*/
map.put("/jquery.js", new DeclaredResource("jquery.js",
MIME_TEXT_JAVASCRIPT + "; charset='UTF-8'"));
map.put("/jquery.flot.js", new DeclaredResource("jquery.flot.js",
MIME_TEXT_JAVASCRIPT + "; charset='UTF-8'"));
map.put("/excanvas.pack.js", new DeclaredResource("excanvas.pack.js",
MIME_TEXT_JAVASCRIPT + "; charset='UTF-8'"));
this.allowedClassPathResources = Collections
.unmodifiableMap(Collections.synchronizedMap(map));
}
@Override
public Response doGet(final Request req) throws Exception {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(
2 * Bytes.kilobyte32);
final String charset = UTF8;
final InputStream is;
/*
* If the request uri is one of the pre-declared resources then we send
* that resource.
*/
final DeclaredResource decl = allowedClassPathResources.get(req.uri);
if (decl != null) {
// send that resource.
return sendClasspathResource(decl);
}
/*
* Materialization the CounterSet iff necessary or stale.
*
* Note: This bit needs to be single threaded to avoid concurrent
* requests causing concurrent materialization of the counter set.
*/
final ICounterSelector counterSelector;
synchronized(this) {
final long now = System.currentTimeMillis();
final long elapsed = now - lastTimestamp;
if (counterSet == null || elapsed > minUpdateLatency/* ms */) {
counterSet = accessor.getCounters();
}
counterSelector = new CounterSetSelector(counterSet);
}
/*
* Obtain a renderer.
*
* @todo This really should pass in the Accept header and our own list
* of preferences and do CONNEG for (X)HMTL vs XML vs text/plain.
*
* @todo if controller state error then send HTTP_BAD_REQUEST
*
* @todo Write XSL and stylesheet for interactive browsing of the
* CounterSet XML?
*/
final String mimeType = MIME_TEXT_HTML;
final IRenderer renderer;
{
// build model of the controller state.
final URLQueryModel model = URLQueryModel.getInstance(getService(),
req.uri, req.params, req.headers);
renderer = RendererFactory.get(model, counterSelector, mimeType);
}
/*
* Render the counters as specified by the query for the negotiated MIME
* type.
*/
{
final OutputStreamWriter w = new OutputStreamWriter(baos);
// render the view.
renderer.render(w);
w.flush();
is = new ByteArrayInputStream(baos.toByteArray());
}
final Response r = new Response(HTTP_OK, mimeType + "; charset='"
+ charset + "'", is);
/*
* Sets the cache behavior -- the data should be good for up to 60
* seconds unless you change the query parameters. These cache control
* parameters SHOULD indicate that the response is valid for 60 seconds,
* that the client must revalidate, and that the response is cachable
* even if the client was authenticated.
*/
r.addHeader("Cache-Control", "max-age=60, must-revalidate, public");
// to disable caching.
// r.addHeader("Cache-Control", "no-cache");
return r;
}
/**
* Send a resource from the classpath.
*
* @param decl
* A pre-declared resource.
*
* @return The {@link Response} which will send that resource.
*
* @throws IOException
* if the resource is not found on the classpath.
*/
final private Response sendClasspathResource(final DeclaredResource decl)
throws IOException {
if (decl == null)
throw new IllegalArgumentException();
if (log.isInfoEnabled())
log.info("Serving: " + decl.localResource + " as " + decl.mimeType);
/*
* Note: This response may be cached.
*
* Note: The Response will consume and close the InputStream.
*/
final Response r = new Response(HTTP_OK, decl.mimeType, decl
.getInputStream());
return r;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy