| /* |
| * 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("':'',"); |
| } |
| |
| } |
| } |