package com.philemonworks.critter;
import com.jamonapi.MonitorFactory;
import com.philemonworks.critter.rule.Rule;
import com.philemonworks.critter.rule.RuleConverter;
import com.philemonworks.critter.ui.HelpPage;
import com.philemonworks.critter.ui.HomePage;
import com.philemonworks.critter.ui.RecordingsPage;
import com.philemonworks.critter.ui.SiteLayout;
import com.philemonworks.selfdiagnose.SelfDiagnose;
import com.philemonworks.selfdiagnose.output.DiagnoseRunReporter;
import com.philemonworks.selfdiagnose.output.HTMLReporter;
import org.apache.commons.lang3.StringUtils;
import org.rendershark.core.logging.LoggerManager;
import org.rendershark.http.HttpServer;
import org.rendersnake.HtmlCanvas;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.List;
@Path("/")
public class TrafficResource {
private static final Logger LOG = LoggerFactory.getLogger(TrafficResource.class);
@Inject
TrafficManager trafficManager;
@Inject
LoggerManager loggerManager;
@Inject
@Named("Proxy")
HttpServer proxyServer;
@Inject
@Named("proxy.host")
String proxyHost;
@Inject
@Named("traffic.port")
String port;
@GET
@Produces("text/html")
public Response home() throws IOException {
List<Rule> rules;
try {
rules = trafficManager.getAllRules();
} catch (Exception ex) {
LOG.error("Failed to retrieve rules", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
HtmlCanvas html = new HtmlCanvas();
html.getPageContext()
.withObject("rules", rules)
.withBoolean("proxy.started", this.proxyServer.isStarted());
html.render(new SiteLayout(new HomePage()));
return Response.ok().entity(html.toHtml()).build();
}
@GET
@Produces("application/xml")
@Path("/rules/{id}")
public Response getRule(@PathParam("id") String id) {
String xml = null;
Rule rule = null;
try {
rule = this.trafficManager.getRule(id);
if (null != rule)
xml = RuleConverter.toXml(rule);
} catch (Exception ex) {
LOG.error("Failed to retrieve rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
if (null == rule) {
return Response.status(404).build();
} else {
return Response.ok().entity(xml).build();
}
}
@POST
@Path("/rules/{id}/enable")
public Response enableRule(@PathParam("id") String id) {
Rule rule = null;
try {
rule = this.trafficManager.getRule(id);
if (null != rule) {
// TODO: Afstemmen Ernest m.b.t. cached rules in MongoDb en enabled/disablen van rules.
rule.enabled = true;
trafficManager.addOrReplaceRule(rule);
}
} catch (Exception ex) {
LOG.error("Failed to enable rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
if (null == rule) {
return Response.status(404).build();
} else {
return Response.ok().build();
}
}
@POST
@Path("/rules/{id}/disable")
public Response disableRule(@PathParam("id") String id) {
Rule rule = null;
try {
rule = this.trafficManager.getRule(id);
if (null != rule) {
rule.enabled = false;
trafficManager.addOrReplaceRule(rule);
}
} catch (Exception ex) {
LOG.error("Failed to disable rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
if (null == rule) {
return Response.status(404).build();
} else {
return Response.ok().build();
}
}
@POST
@Path("/rules/{id}/trace-on")
public Response traceOnRule(@PathParam("id") String id) {
Rule rule = null;
try {
rule = this.trafficManager.getRule(id);
if (null != rule) {
// TODO: Afstemmen Ernest m.b.t. cached rules in MongoDb en enabled/disablen van rules.
rule.tracing = true;
trafficManager.addOrReplaceRule(rule);
}
} catch (Exception ex) {
LOG.error("Failed to trace-on rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
if (null == rule) {
return Response.status(404).build();
} else {
return Response.ok().build();
}
}
@POST
@Path("/rules/{id}/trace-off")
public Response traceOffRule(@PathParam("id") String id) {
Rule rule = null;
try {
rule = this.trafficManager.getRule(id);
if (null != rule) {
rule.tracing = false;
trafficManager.addOrReplaceRule(rule);
}
} catch (Exception ex) {
LOG.error("Failed to trace-off rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
if (null == rule) {
return Response.status(404).build();
} else {
return Response.ok().build();
}
}
@DELETE
@Path("/rules/{id}")
public Response deleteRule(@PathParam("id") String id) {
try {
this.trafficManager.deleteRule(id);
} catch (Exception ex) {
LOG.error("Failed to delete rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
return Response.ok().build();
}
@GET
@Produces("application/xml")
@Path("/rules")
public Response getRules() {
try {
List<Rule> rules = this.trafficManager.getAllRules();
String xml = RuleConverter.toXml(rules);
return Response.ok().entity(xml).build();
} catch (Exception ex) {
LOG.error("Failed to retrieve rules", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
@GET
@Produces("text/html")
@Path("/recordings")
public Response getRecordings(@QueryParam("host") String host, @QueryParam("method") String method) throws IOException, URISyntaxException {
if (StringUtils.isNotBlank(host) && StringUtils.isNotBlank(method)) {
return Response.temporaryRedirect(new URI(String.format("/recordings/%s/%s", host, method))).build();
} else {
HtmlCanvas html = new HtmlCanvas();
html.getPageContext().withString("host", host).withString("method", method);
html.render(new SiteLayout(new RecordingsPage()));
return Response.ok().entity(html.toHtml()).build();
}
}
@GET
@Produces("application/xml")
@Path("/recordings/{host}/{method}")
public Response getRecordings(
@PathParam("host") String host,
@PathParam("method") String method,
@QueryParam("path") @DefaultValue("") String path,
@QueryParam("query") @DefaultValue("") String query,
@QueryParam("limit") @DefaultValue("0") int howMany
) {
try {
List<Recording> records = this.trafficManager.recordingDao.search(host, method, path, query, howMany);
String xml = RecordingConverter.toXml(records);
return Response.ok().entity(xml).build();
} catch (Exception ex) {
LOG.error("Failed to retrieve recordings", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
@DELETE
@Path("/recordings/{host}")
public Response deleteRecordingsToHost(@PathParam("host") String host) {
try {
this.trafficManager.recordingDao.deleteRecordingsByHost(host);
return Response.ok().build();
} catch (Exception ex) {
LOG.error("Failed to delete recordings", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
}
@POST
@Consumes("application/xml")
@Path("/rules")
public Response putRule(InputStream inputStream) {
URI uri = null;
try {
Object oneOrMoreRules = RuleConverter.fromXml(inputStream);
if (oneOrMoreRules instanceof Rule) {
Rule rule = (Rule) oneOrMoreRules;
this.trafficManager.addOrReplaceRule(rule);
uri = this.buildURI("/rules/" + (rule.id.replaceAll("\\s", "%20")));
} else { // it must be a list
@SuppressWarnings("unchecked")
List<Rule> rules = (List<Rule>) oneOrMoreRules;
for (Rule each : rules) {
this.trafficManager.addOrReplaceRule(each);
}
uri = this.buildURI("/rules");
}
} catch (Exception ex) {
LOG.error("Failed to create rule", ex);
return Response.serverError().entity(ex.getMessage()).build();
}
return Response.created(uri).build();
}
@PUT
@Consumes("application/xml")
@Path("/rules/{id}")
public Response putRule(@PathParam("id") String id, InputStream inputStream) {
Rule rule = null;
URI uri = null;
try {
rule = (Rule) RuleConverter.fromXml(inputStream);
rule.id = id;
this.trafficManager.addOrReplaceRule(rule);
uri = this.buildURI("/rules/" + URLEncoder.encode(rule.id, "utf8"));
} catch (Exception ex) {
LOG.error("Failed to create rule:" + id, ex);
return Response.serverError().entity(ex.getMessage()).build();
}
return Response.created(uri).build();
}
@GET
@Path("/internal/selfdiagnose.html")
@Produces("text/html")
public Response reportSelfDiagnose() {
DiagnoseRunReporter reporter = new HTMLReporter();
SelfDiagnose.runTasks(reporter);
return Response.ok().entity(reporter.getContent()).build();
}
private URI buildURI(String path) throws URISyntaxException {
return new URI("http://" + proxyHost + ":" + port + path);
}
@POST
@Path("/internal/logger/{name}/{level}")
public Response changeLogger(@PathParam("name") String name, @PathParam("level") String level) {
this.loggerManager.setLoggerLevel(name, level);
return Response.ok().entity(this.loggerManager.getLoggerLevel(name)).build();
}
@GET
@Path("/example")
@Produces("application/xml")
public Response example() {
return Response.ok().entity(this.getClass().getResourceAsStream("/rule-example.xml")).build();
}
@GET
@Path("/help.html")
@Produces("text/html")
public Response help() throws IOException {
HtmlCanvas html = new HtmlCanvas();
html.getPageContext()
.withBoolean("proxy.started", this.proxyServer.isStarted());
html.render(new SiteLayout(new HelpPage()));
return Response.ok().entity(html.toHtml()).build();
}
@POST
@Path("/proxy/start")
@Produces("text/plain")
public Response startUpProxyServer() {
this.proxyServer.startUp();
return Response.ok("proxy server is started:" + this.proxyServer.isStarted()).build();
}
@POST
@Path("/proxy/stop")
@Produces("text/plain")
public Response shutDownProxyServer() {
this.proxyServer.shutDown();
return Response.ok("proxy server is started:" + this.proxyServer.isStarted()).build();
}
@GET
@Path("internal/stats.html")
@Produces("text/html")
public Response statistics(@QueryParam("flush") @DefaultValue("false") boolean doFlush) throws IOException {
if (doFlush) MonitorFactory.reset();
HtmlCanvas html = new HtmlCanvas();
html.html().body().write(MonitorFactory.getReport(), false)._body()._html();
return Response.ok().entity(html.toHtml()).build();
}
}