/* * 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; } }