blob: 15da4e41fbdff72c93cc386957cf4171767d889b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.sling.reqanalyzer.impl;
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.sling.reqanalyzer.impl.gui.MainFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings("serial")
public class RequestAnalyzerWebConsole extends HttpServlet {
private static final String WINDOW_MARKER = ".showWindow";
private static final String RAW_FILE_MARKER = ".txt";
private static final String ZIP_FILE_MARKER = ".txt.zip";
/** default log */
private final Logger log = LoggerFactory.getLogger(getClass());
private final File logFile;
private MainFrame frame;
RequestAnalyzerWebConsole(final File logFile) {
this.logFile = logFile;
}
void dispose() {
if (this.frame != null) {
MainFrame frame = this.frame;
this.frame = null;
AWTEvent e = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e);
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getRequestURI().endsWith(RAW_FILE_MARKER) || req.getRequestURI().endsWith(ZIP_FILE_MARKER)) {
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(this.logFile);
output = resp.getOutputStream();
if (req.getRequestURI().endsWith(ZIP_FILE_MARKER)) {
ZipOutputStream zip = new ZipOutputStream(output);
zip.setLevel(Deflater.BEST_SPEED);
ZipEntry entry = new ZipEntry(this.logFile.getName());
entry.setTime(this.logFile.lastModified());
entry.setMethod(ZipEntry.DEFLATED);
zip.putNextEntry(entry);
output = zip;
resp.setContentType("application/zip");
} else {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Content-Length", String.valueOf(this.logFile.length())); // might be bigger than
}
resp.setDateHeader("Last-Modified", this.logFile.lastModified());
IOUtils.copy(input, output);
} catch (IOException ioe) {
throw new ServletException("Cannot create copy of log file", ioe);
} finally {
IOUtils.closeQuietly(input);
if (output instanceof ZipOutputStream) {
((ZipOutputStream) output).closeEntry();
((ZipOutputStream) output).finish();
}
}
resp.flushBuffer();
} else if (req.getRequestURI().endsWith(WINDOW_MARKER)) {
if (canOpenSwingGui(req)) {
showWindow();
}
String target = req.getRequestURI();
target = target.substring(0, target.length() - WINDOW_MARKER.length());
resp.sendRedirect(target);
resp.flushBuffer();
} else {
super.service(req, resp);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
PrintWriter pw = resp.getWriter();
final String fileSize = this.formatByteSize(this.logFile.length());
pw.printf("<p class='statline ui-state-highlight'>Request Log Size %s</p>%n", fileSize);
pw.println("<table class='nicetable ui-widget'>");
pw.println(" <thead>");
pw.println(" <tr>");
pw.println(" <th class='ui-widget-header'>Name</th>");
pw.println(" <th class='ui-widget-header'>Last Update</th>");
pw.println(" <th class='ui-widget-header'>Size</th>");
pw.println(" <th class='ui-widget-header'>Action</th>");
pw.println(" </tr>");
pw.println("</thead>");
pw.println("<tbody>");
pw.println("<tr>");
pw.printf("<td><a href='${pluginRoot}%s'>%s</a> (<a href='${pluginRoot}%s'>zipped</a>)</td>%n", RAW_FILE_MARKER, this.logFile.getName(), ZIP_FILE_MARKER);
pw.printf("<td>%s</td><td>%s</td>", new Date(this.logFile.lastModified()), fileSize);
pw.println("<td><ul class='icons ui-widget'>");
if (canOpenSwingGui(req)) {
pw.printf(
"<li title='Analyze Now' class='dynhover ui-state-default ui-corner-all'><a href='${pluginRoot}%s'><span class='ui-icon ui-icon-wrench'></span></a></li>%n",
WINDOW_MARKER);
// pw.printf(" (<a href='${pluginRoot}%s'>Analyze Now</a>)", WINDOW_MARKER);
}
// pw.println("<li title='Remove File' class='dynhover ui-state-default ui-corner-all'><span class='ui-icon ui-icon-trash'></span></li>");
pw.println("</ul></td>");
pw.println("</tr></tbody>");
pw.println("</table>");
}
private synchronized void showWindow() throws ServletException, IOException {
if (this.frame == null) {
final File toAnalyze = this.getLogFileCopy();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
MainFrame frame = new MainFrame(toAnalyze, Integer.MAX_VALUE, screenSize);
// exit the application if the main frame is closed
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// remove the tmp file we showed
if (toAnalyze.exists()) {
toAnalyze.delete();
}
// clear the window reference
RequestAnalyzerWebConsole.this.frame = null;
}
});
this.frame = frame;
}
// make sure the window is visible, try to bring to the front
this.frame.setVisible(true);
this.frame.toFront();
}
private boolean canOpenSwingGui(final ServletRequest request) {
try {
return !GraphicsEnvironment.isHeadless()
&& InetAddress.getByName(request.getLocalAddr()).isLoopbackAddress();
} catch (UnknownHostException e) {
// unexpected, but still fall back to fail-safe false
return false;
}
}
private final File getLogFileCopy() throws ServletException {
File result = null;
InputStream input = null;
OutputStream output = null;
try {
result = File.createTempFile(getServletName(), ".tmp");
input = new FileInputStream(this.logFile);
output = new FileOutputStream(result);
IOUtils.copy(input, output);
return result;
} catch (IOException ioe) {
throw new ServletException("Cannot create copy of log file", ioe);
} finally {
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(output);
}
}
private String formatByteSize(final long value) {
final String suffix;
final String suffixedValue;
if (value >= 0) {
final BigDecimal KB = new BigDecimal(1000L);
final BigDecimal MB = new BigDecimal(1000L * 1000);
final BigDecimal GB = new BigDecimal(1000L * 1000 * 1000);
BigDecimal bd = new BigDecimal(value);
if (bd.compareTo(GB) > 0) {
bd = bd.divide(GB);
suffix = "GB";
} else if (bd.compareTo(MB) > 0) {
bd = bd.divide(MB);
suffix = "MB";
} else if (bd.compareTo(KB) > 0) {
bd = bd.divide(KB);
suffix = "kB";
} else {
suffix = "B";
}
suffixedValue = bd.setScale(2, RoundingMode.UP).toString();
} else {
suffixedValue = "n/a";
suffix = "";
}
return suffixedValue + suffix;
}
}