This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-reqanalyzer.git
commit a8510744e9025405e01c19b5c80ba707e99fdbb6 Author: Felix Meschberger <[email protected]> AuthorDate: Tue Sep 24 07:58:42 2013 +0000 SLING-3099 Expose the request analyzer log file through the Web Console - Allow for plain text download - Allow for zipped download - Support launching the Swing GUI git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1525810 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 6 + .../reqanalyzer/impl/RequestAnalysisLogger.java | 31 ++- .../impl/RequestAnalyzerWebConsole.java | 250 +++++++++++++++++++++ .../apache/sling/reqanalyzer/impl/gui/Main.java | 10 + .../sling/reqanalyzer/impl/gui/MainFrame.java | 12 +- 5 files changed, 295 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 55be5ec..265702f 100644 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,12 @@ <artifactId>slf4j-simple</artifactId> </dependency> <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + <scope>provided</scope> + </dependency> + <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.8.2</version> diff --git a/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java b/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java index bcf98c4..db1efa6 100644 --- a/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java +++ b/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; +import java.util.Hashtable; import java.util.Iterator; import javax.servlet.Filter; @@ -45,7 +46,9 @@ import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper; import org.apache.sling.engine.EngineConstants; import org.apache.sling.settings.SlingSettingsService; +import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; @Component(metatype = false) @Service @@ -59,18 +62,40 @@ public class RequestAnalysisLogger implements Filter { private BufferedWriter logFile; - @SuppressWarnings("unused") + private RequestAnalyzerWebConsole requestAnalyzerWebConsole; + private ServiceRegistration webConsolePlugin; + + @SuppressWarnings({ "serial" }) @Activate - private void activate() throws IOException { + private void activate(final BundleContext ctx) throws IOException { final File logFile = new File(settings.getSlingHomePath(), "logs/requesttracker.txt"); logFile.getParentFile().mkdirs(); final FileOutputStream out = new FileOutputStream(logFile, true); this.logFile = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); + + this.requestAnalyzerWebConsole = new RequestAnalyzerWebConsole(logFile); + this.webConsolePlugin = ctx.registerService("javax.servlet.Servlet", this.requestAnalyzerWebConsole, + new Hashtable<String, Object>() { + { + put("felix.webconsole.label", "requestanalyzer"); + put("felix.webconsole.title", "Request Analyzer"); + put("felix.webconsole.category", "Sling"); + } + }); } - @SuppressWarnings("unused") @Deactivate private void deactivate() throws IOException { + if (this.webConsolePlugin != null) { + this.webConsolePlugin.unregister(); + this.webConsolePlugin = null; + } + + if (this.requestAnalyzerWebConsole != null) { + this.requestAnalyzerWebConsole.dispose(); + this.requestAnalyzerWebConsole = null; + } + if (this.logFile != null) { this.logFile.close(); this.logFile = null; diff --git a/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java b/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java new file mode 100644 index 0000000..4c473e1 --- /dev/null +++ b/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java @@ -0,0 +1,250 @@ +/* + * 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.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javax.servlet.ServletException; +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 final Set<MainFrame> frames; + + RequestAnalyzerWebConsole(final File logFile) { + this.logFile = logFile; + this.frames = new HashSet<MainFrame>(); + } + + void dispose() { + final Set<MainFrame> frames = new HashSet<MainFrame>(this.frames); + this.frames.clear(); + for (MainFrame mainFrame : frames) { + AWTEvent e = new WindowEvent(mainFrame, 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) && !GraphicsEnvironment.isHeadless()) { + + String target = req.getRequestURI(); + target = target.substring(0, target.length() - WINDOW_MARKER.length()); + + showWindow(); + 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 (!GraphicsEnvironment.isHeadless()) { + 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 void showWindow() throws ServletException, IOException { + final File toAnalyze = this.getLogFileCopy(); + + final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + MainFrame frame = new MainFrame(toAnalyze, Integer.MAX_VALUE, screenSize); + frame.setVisible(true); + frame.toFront(); + + // exit the application if the main frame is closed + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + if (toAnalyze.exists()) { + toAnalyze.delete(); + } + } + }); + + this.frames.add(frame); + } + + 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; + } + +} diff --git a/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java b/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java index c0efcca..0459b04 100644 --- a/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java +++ b/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java @@ -21,6 +21,8 @@ package org.apache.sling.reqanalyzer.impl.gui; 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.IOException; @@ -54,6 +56,14 @@ public class Main { MainFrame frame = new MainFrame(file, limit, screenSize); frame.setVisible(true); + + // exit the application if the main frame is closed + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); } } diff --git a/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java b/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java index 7feb4f1..eccf98c 100644 --- a/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java +++ b/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java @@ -20,8 +20,6 @@ package org.apache.sling.reqanalyzer.impl.gui; import java.awt.Color; import java.awt.Dimension; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -49,7 +47,7 @@ public class MainFrame extends JFrame { JTextPane text = Util.showStartupDialog("Reading from " + file, screenSize); final RequestTrackerFile dm = new RequestTrackerFile(file, limit, text); - + final JTable table = new JTable(dm); table.setAutoCreateRowSorter(true); table.setGridColor(Color.GRAY); @@ -65,14 +63,6 @@ public class MainFrame extends JFrame { setTitle(file.getPath()); - // exit the application if the main frame is closed - addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - // setup location and size and ensure updating preferences Util.setupComponentLocationSize(this, MAIN_X, MAIN_Y, MAIN_WIDTH, MAIN_HEIGHT, (int) screenSize.getWidth() / 4, 0, (int) screenSize.getWidth() / 2, (int) screenSize.getHeight() / 4); -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
