/* * 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.felix.webconsole.plugins.memoryusage.internal; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.NoSuchElementException; import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.felix.webconsole.AbstractWebConsolePlugin; import org.apache.felix.webconsole.AttachmentProvider; import org.apache.felix.webconsole.ConfigurationPrinter; import org.apache.felix.webconsole.DefaultVariableResolver; import org.apache.felix.webconsole.WebConsoleUtil; import org.osgi.service.log.LogService; @SuppressWarnings("serial") class MemoryUsagePanel extends AbstractWebConsolePlugin implements ConfigurationPrinter, AttachmentProvider { private final MemoryUsageSupport support; MemoryUsagePanel(final MemoryUsageSupport support) { this.support = support; activate(support.getBundleContext()); } // ---------- AbstractWebConsolePlugin @Override public String getLabel() { return MemoryUsageConstants.LABEL; } @Override public String getTitle() { return "%dump.title"; } @SuppressWarnings("unchecked") @Override protected void renderContent(HttpServletRequest req, HttpServletResponse res) throws IOException { final PrintWriter pw = res.getWriter(); final StringBuilder statusBuf = new StringBuilder(1024); statusBuf.append('{'); final StringBuilder filesBuf = new StringBuilder(1024); filesBuf.append('['); final File[] files = support.getDumpFiles(); if (files != null) { long totalSize = 0; for (File file : files) { filesBuf.append('{'); filesBuf.append("'name':'").append(file.getName()); filesBuf.append("',").append("'date':").append(file.lastModified()); support.formatNumber(filesBuf, "size", file.length()); filesBuf.append("},"); totalSize += file.length(); } statusBuf.append("'files':").append(files.length); support.formatNumber(statusBuf, "total", totalSize); } else { statusBuf.append("'files':0,'total':0"); } filesBuf.append(']'); statusBuf.append('}'); JsonPrintHelper jph = new JsonPrintHelper(); support.printOverallMemory(jph); DefaultVariableResolver resolver = (DefaultVariableResolver) WebConsoleUtil.getVariableResolver(req); resolver.put("__files__", filesBuf.toString()); resolver.put("__status__", statusBuf.toString()); resolver.put("__threshold__", String.valueOf(support.getThreshold())); resolver.put("__interval__", String.valueOf(support.getInterval())); resolver.put("__overall__", jph.getString()); resolver.put("__pools__", support.getMemoryPoolsJson()); String template = readTemplateFile("/templates/memoryusage.html"); pw.println(template); } // ---------- Configuration Printer public void printConfiguration(PrintWriter pw) { support.printMemory(new PrintWriterPrintHelper(pw)); } // ---------- AttachmentProvider public URL[] getAttachments(String mode) { if (ConfigurationPrinter.MODE_ZIP.equals(mode)) { File[] dumpFiles = support.getDumpFiles(); if (dumpFiles != null && dumpFiles.length > 0) { URL[] attachs = new URL[dumpFiles.length]; for (int i = 0; i < dumpFiles.length; i++) { try { attachs[i] = dumpFiles[i].toURI().toURL(); } catch (MalformedURLException mue) { // not expected ... } } return attachs; } } // not ZIP mode, return nothing return null; } // ---------- GenericServlet @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final DumpFile dumpFile = getDumpFile(request); if (dumpFile != null) { spool(dumpFile.dumpFile, response, dumpFile.compress); } super.doGet(request, response); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if ("DELETE".equals(req.getParameter("X-Request-Method"))) { doDelete(req, resp); } else { String command = WebConsoleUtil.getParameter(req, "command"); if ("dump".equals(command)) { resp.setContentType("text/plain; charset=UTF-8"); try { File file = support.dumpHeap(null, false); resp.getWriter().print("Dumped heap to " + file.getName()); } catch (NoSuchElementException e) { resp.getWriter().print( "Failed dumping the heap, JVM does not provide known mechanism to create a Heap Dump"); support.log(LogService.LOG_ERROR, "Heap Dump creation failed: JVM has no known Heap Dump API"); } } else if ("gc".equals(command)) { System.gc(); } else if ("threshold".equals(command)) { try { int threshold = Integer.parseInt(req.getParameter("threshold")); support.setThreshold(threshold); } catch (Exception e) { // ignore } resp.sendRedirect(req.getRequestURI()); } else if ("interval".equals(command)) { try { int interval = Integer.parseInt(req.getParameter("interval")); support.setInterval(interval); } catch (Exception e) { // ignore } resp.sendRedirect(req.getRequestURI()); } else { super.doPost(req, resp); } } } @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException { final DumpFile dumpFile = getDumpFile(request); if (dumpFile != null) { dumpFile.dumpFile.delete(); response.setStatus(HttpServletResponse.SC_OK); } else { response.sendError(HttpServletResponse.SC_FORBIDDEN); } } // ---------- internal private DumpFile getDumpFile(final HttpServletRequest request) { final String pathInfo = request.getPathInfo(); if (pathInfo != null && !pathInfo.endsWith(getLabel())) { final int lastSlash = pathInfo.lastIndexOf('/'); if (lastSlash > 0) { String label = pathInfo.substring(lastSlash + 1); boolean isZip = false; if (label.endsWith(".zip")) { label = label.substring(0, label.length() - 4); isZip = true; } File dumpFile = support.getDumpFile(label); if (dumpFile != null) { return new DumpFile(dumpFile, isZip); } } } return null; } private void spool(final File dumpFile, final HttpServletResponse response, boolean compress) throws IOException { InputStream ins = null; try { ins = new FileInputStream(dumpFile); response.setDateHeader("Last-Modified", dumpFile.lastModified()); WebConsoleUtil.setNoCache(response); OutputStream out = response.getOutputStream(); if (compress) { ZipOutputStream zip = new ZipOutputStream(out); zip.setLevel(Deflater.BEST_SPEED); ZipEntry entry = new ZipEntry(dumpFile.getName()); entry.setTime(dumpFile.lastModified()); entry.setMethod(ZipEntry.DEFLATED); zip.putNextEntry(entry); out = zip; // zip output with unknown length response.setContentType("application/zip"); } else { String type = getServletContext().getMimeType(dumpFile.getName()); if (type == null) { type = "application/octet-stream"; } response.setContentType(type); response.setHeader("Content-Length", String.valueOf(dumpFile.length())); // might be bigger than // int } byte[] buf = new byte[32768]; int rd = 0; while ((rd = ins.read(buf)) >= 0) { out.write(buf, 0, rd); } if (compress) { out.flush(); ((ZipOutputStream) out).closeEntry(); ((ZipOutputStream) out).finish(); } } finally { if (ins != null) { try { ins.close(); } catch (IOException ignore) { // ignore } } } } private static class DumpFile { final File dumpFile; final boolean compress; DumpFile(final File dumpFile, final boolean compress) { this.dumpFile = dumpFile; this.compress = compress; } } private static class PrintWriterPrintHelper implements MemoryUsageSupport.PrintHelper { private static final String INDENTS = " "; private final PrintWriter pw; private String indent; PrintWriterPrintHelper(final PrintWriter pw) { this.pw = pw; this.indent = ""; } public void title(String title, int level) { pw.printf("%n%s%s%n", getIndent(level), title); indent = getIndent(level + 1); } public void val(String value) { pw.printf("%s%s%n", indent, value); } public void keyVal(final String key, final Object value) { if (value == null) { val(key); } else { pw.printf("%s%s: %s%n", indent, key, value); } } private static String getIndent(final int level) { final int indent = 2 * level; if (indent > INDENTS.length()) { return INDENTS; } return INDENTS.substring(0, indent); } } private static class JsonPrintHelper implements MemoryUsageSupport.PrintHelper { private final StringBuilder buf; JsonPrintHelper() { buf = new StringBuilder(); buf.append('{'); } String getString() { final String result = buf.append('}').toString(); buf.delete(1, buf.length()); return result; } public void title(String title, int level) { } public void keyVal(String key, Object value) { if (value == null) { val(key); } else { buf.append('\''); buf.append(key); buf.append("':'"); buf.append(value); buf.append("',"); } } public void val(String value) { buf.append("'"); buf.append(value); buf.append("':'',"); } } }