/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bull.javamelody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.bull.javamelody.HtmlInjectorResponseStream.HtmlToInject;
/**
* Injection du script Boomerang avant le tag <code></body></code> pour le Real User Monitoring (RUM).
* @author Emeric Vernat
*/
final class RumInjector implements HtmlToInject {
private static final String BOOMERANG_FILENAME = "boomerang.min.js";
private final long start = System.currentTimeMillis();
private final String rumUrl;
private final String requestName;
private RumInjector(String rumUrl, String requestName) {
super();
this.rumUrl = rumUrl;
this.requestName = requestName;
}
static HttpServletResponse createRumResponseWrapper(HttpServletRequest httpRequest,
HttpServletResponse httpResponse, String requestName) {
if (HtmlInjectorServletResponseWrapper.acceptsRequest(httpRequest)) {
final String rumUrl = getRumUrlForBrowser(requestName);
final HtmlToInject htmlToInject = new RumInjector(rumUrl, requestName);
return new HtmlInjectorServletResponseWrapper(httpRequest, httpResponse, htmlToInject);
}
return httpResponse;
}
private static String getRumUrlForBrowser(String requestName) {
// for the RUM URL, we want the monitoring URL (<contextPath>/monitoring relative to the hostname),
// but from the browser it may not be correct:
// for example with an Apache proxy, the client may not have <contextPath> in the URL.
// (And note that httpRequest.getRequestURI() and httpRequest.getRequestUrl() include the <contextPath>,
// if there is an Apache proxy or not, so they do not help.)
// So, based on the requestName, we use a relative monitoring URL for the browser whatever the <contextPath>
// requestName is of the form "/path/to/file GET"
// first remove " GET" part
final int lastIndexOfSpace = requestName.lastIndexOf(' ');
assert lastIndexOfSpace != -1;
String tmp = requestName.substring(0, lastIndexOfSpace);
// replace each subpath by ".."
while (tmp.indexOf("//") != -1) {
tmp = tmp.replaceAll("//", "/");
}
tmp = tmp.replaceAll("/[^/]*", "/..");
// remove first subpath
if (tmp.startsWith("/..")) {
tmp = tmp.substring(3);
}
String tmp2 = tmp + Parameters.getMonitoringPath();
// remove first '/', including when tmp == Parameters.getMonitoringPath()
while (tmp2.length() > 0 && tmp2.charAt(0) == '/') {
tmp2 = tmp2.substring(1);
}
return tmp2;
}
@Override
public String getContent() {
// approximation of server duration (may not be the real server duration, but not far in general)
final long serverTime = System.currentTimeMillis() - start;
return "\n<script src='" + rumUrl + "?resource=" + BOOMERANG_FILENAME
+ "'></script>\n<script>BOOMR.init({beacon_url: '" + rumUrl
+ "?part=rum', log: null});\nBOOMR.addVar('requestName', \"" + requestName
+ "\");\n" + "BOOMR.addVar('serverTime', " + serverTime + ");\n</script>\n";
}
@Override
public String getBeforeTag() {
// we suppose lowercase
return "</body>";
}
static boolean isRumResource(String resourceName) {
return BOOMERANG_FILENAME.equals(resourceName);
}
static void addRumHit(HttpServletRequest httpRequest, Counter httpCounter) {
final String requestName = httpRequest.getParameter("requestName");
if (requestName == null) {
return;
}
try {
final long serverTime = Long.parseLong(httpRequest.getParameter("serverTime"));
final long timeToFirstByte = Long
.parseLong(httpRequest.getParameter("timeToFirstByte"));
final long domProcessing = Long.parseLong(httpRequest.getParameter("domProcessing"));
final long pageRendering = Long.parseLong(httpRequest.getParameter("pageRendering"));
final long networkTime = Math.max(timeToFirstByte - serverTime, 0);
httpCounter.addRumHit(requestName, networkTime, domProcessing, pageRendering);
} catch (final NumberFormatException e) {
return;
}
}
}