You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2013/09/24 09:58:42 UTC

svn commit: r1525810 - in /sling/trunk/contrib/extensions/reqanalyzer: ./ src/main/java/org/apache/sling/reqanalyzer/impl/ src/main/java/org/apache/sling/reqanalyzer/impl/gui/

Author: fmeschbe
Date: Tue Sep 24 07:58:42 2013
New Revision: 1525810

URL: http://svn.apache.org/r1525810
Log:
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

Added:
    sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java
Modified:
    sling/trunk/contrib/extensions/reqanalyzer/pom.xml
    sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java
    sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java
    sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java

Modified: sling/trunk/contrib/extensions/reqanalyzer/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/reqanalyzer/pom.xml?rev=1525810&r1=1525809&r2=1525810&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/reqanalyzer/pom.xml (original)
+++ sling/trunk/contrib/extensions/reqanalyzer/pom.xml Tue Sep 24 07:58:42 2013
@@ -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>

Modified: sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java?rev=1525810&r1=1525809&r2=1525810&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java (original)
+++ sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalysisLogger.java Tue Sep 24 07:58:42 2013
@@ -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.SlingHttpSer
 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 imple
 
     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;

Added: sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java?rev=1525810&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java (added)
+++ sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/RequestAnalyzerWebConsole.java Tue Sep 24 07:58:42 2013
@@ -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;
+    }
+
+}

Modified: sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java?rev=1525810&r1=1525809&r2=1525810&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java (original)
+++ sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/Main.java Tue Sep 24 07:58:42 2013
@@ -21,6 +21,8 @@ package org.apache.sling.reqanalyzer.imp
 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);
+            }
+        });
     }
 
 }

Modified: sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java?rev=1525810&r1=1525809&r2=1525810&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java (original)
+++ sling/trunk/contrib/extensions/reqanalyzer/src/main/java/org/apache/sling/reqanalyzer/impl/gui/MainFrame.java Tue Sep 24 07:58:42 2013
@@ -20,8 +20,6 @@ package org.apache.sling.reqanalyzer.imp
 
 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);