001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.management;
018
019import java.io.*;
020
021import org.apache.juneau.http.response.*;
022import org.apache.juneau.rest.server.*;
023import org.apache.juneau.rest.server.servlet.*;
024
025/**
026 * Child-resource flavor of the diagnostic dump endpoints ({@code /threaddump}, {@code /heapdump}).
027 *
028 * <p>
029 * Mounts as a routed child via {@link Rest#children() @Rest(children=DumpsResource.class)} and delegates to a
030 * shared {@link DumpsManager} worker &mdash; the same logic the {@link DumpsMixin mixin} flavor uses, so the
031 * two forms cannot drift.  Extends {@link BasicRestResource} (mirrors {@code HealthResource}).
032 *
033 * <p>
034 * <b>Both dumps are deny-by-default</b> &mdash; they respond {@code 403 Forbidden} unless a
035 * {@link DumpsSettings} bean enabling them is registered.  {@code /heapdump} additionally degrades to
036 * {@code 501 Not Implemented} on a JVM without HotSpot heap-dump support.
037 *
038 * <h5 class='section'>See Also:</h5><ul>
039 *    <li class='jc'>{@link DumpsMixin}
040 *    <li class='jc'>{@link DumpsManager}
041 *    <li class='jc'>{@link DumpsSettings}
042 * </ul>
043 *
044 * @since 10.0.0
045 */
046@Rest(path="/dumps")
047@SuppressWarnings({
048   "resource" // getHeapDump() returns a self-deleting InputStream that the REST framework streams to the response and closes.
049})
050public class DumpsResource extends BasicRestResource {
051
052   private final DumpsManager defaultManager = new DumpsManager();
053
054   /**
055    * Returns the {@link DumpsManager} worker, resolved from the bean store when a consumer registers one, else a
056    * built-in default.  Resolving through the bean store lets consumers (and tests) supply an alternate worker
057    * without subclassing.
058    *
059    * @param req The HTTP request (its context's bean store is searched).
060    * @return The resolved manager; never <jk>null</jk>.
061    */
062   protected DumpsManager manager(RestRequest req) {
063      return req.getContext().getBeanStore().getBean(DumpsManager.class).orElse(defaultManager);
064   }
065
066   /**
067    * [GET /threaddump] - Full thread dump from the {@link java.lang.management.ThreadMXBean ThreadMXBean}.
068    *
069    * @param req The HTTP request.
070    * @return The thread dump text.
071    * @throws Forbidden If the thread-dump endpoint is not enabled.
072    */
073   @RestGet(
074      path="/threaddump",
075      summary="Thread dump",
076      description="Renders a full thread dump from the JVM ThreadMXBean.  Disabled by default; opt in via DumpsSettings."
077   )
078   public String getThreadDump(RestRequest req) {
079      var manager = manager(req);
080      if (! manager.resolveSettings(req.getContext()).isThreadDumpEnabled())
081         throw new Forbidden("The /threaddump endpoint is disabled.  Register a DumpsSettings bean with threadDump enabled to use it.");
082      return manager.threadDump();
083   }
084
085   /**
086    * [GET /heapdump] - HotSpot heap dump (hprof) stream.
087    *
088    * @param req The HTTP request.
089    * @param res The HTTP response.
090    * @return A self-deleting stream over the hprof file.
091    * @throws Forbidden If the heap-dump endpoint is not enabled.
092    * @throws NotImplemented If this JVM has no HotSpot heap-dump support.
093    * @throws IOException If the dump could not be produced.
094    */
095   @RestGet(
096      path="/heapdump",
097      summary="Heap dump",
098      description="Produces a HotSpot heap dump (hprof).  Disabled by default; opt in via DumpsSettings."
099   )
100   public InputStream getHeapDump(RestRequest req, RestResponse res) throws IOException {
101      var manager = manager(req);
102      if (! manager.resolveSettings(req.getContext()).isHeapDumpEnabled())
103         throw new Forbidden("The /heapdump endpoint is disabled.  Register a DumpsSettings bean with heapDump enabled to use it.");
104      var stream = manager.heapDumpStream(true);
105      if (stream == null)  // HTT: null only on a non-HotSpot JVM — not reproducible on HotSpot CI.
106         throw new NotImplemented("Heap dumps are not supported on this JVM (no HotSpotDiagnosticMXBean).");
107      res.setHeader("Content-Type", "application/octet-stream");
108      res.setHeader("Content-Disposition", "attachment; filename=\"heapdump.hprof\"");
109      return stream;
110   }
111}