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 * Mixin flavor of the diagnostic dump endpoints ({@code /threaddump}, {@code /heapdump}).
027 *
028 * <p>
029 * Composes into a host resource via {@link Rest#mixins() @Rest(mixins=DumpsMixin.class)} and delegates to a
030 * shared {@link DumpsManager} worker so the mixin and {@link DumpsResource resource} flavors cannot drift.
031 *
032 * <p>
033 * <b>Both dumps are deny-by-default</b> &mdash; they respond {@code 403 Forbidden} unless a
034 * {@link DumpsSettings} bean enabling them is registered in the bean store.  {@code /heapdump} additionally
035 * degrades to {@code 501 Not Implemented} on a JVM without HotSpot heap-dump support.
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='jc'>{@link DumpsResource}
039 *    <li class='jc'>{@link DumpsManager}
040 *    <li class='jc'>{@link DumpsSettings}
041 * </ul>
042 *
043 * @since 10.0.0
044 */
045@Rest
046@SuppressWarnings({
047   "resource" // The heap dump InputStream is returned to the REST framework, which streams and closes it
048})
049public class DumpsMixin extends RestMixin {
050
051   private final DumpsManager defaultManager = new DumpsManager();
052
053   /**
054    * Returns the {@link DumpsManager} worker, resolved from the host bean store when a consumer registers one,
055    * else a built-in default.  Resolving through the bean store lets consumers (and tests) supply an alternate
056    * worker without subclassing.
057    *
058    * @param req The HTTP request (its context's bean store is searched).
059    * @return The resolved manager; never <jk>null</jk>.
060    */
061   protected DumpsManager manager(RestRequest req) {
062      return req.getContext().getBeanStore().getBean(DumpsManager.class).orElse(defaultManager);
063   }
064
065   /**
066    * [GET /threaddump] - Full thread dump from the {@link java.lang.management.ThreadMXBean ThreadMXBean}.
067    *
068    * <p>
069    * <b>Deny-by-default:</b> responds {@code 403} unless enabled via {@link DumpsSettings}.
070    *
071    * @param req The HTTP request.
072    * @return The thread dump text.
073    * @throws Forbidden If the thread-dump endpoint is not enabled.
074    */
075   @RestGet(
076      path="/threaddump",
077      summary="Thread dump",
078      description="Renders a full thread dump from the JVM ThreadMXBean.  Disabled by default; opt in via DumpsSettings."
079   )
080   public String getThreadDump(RestRequest req) {
081      var manager = manager(req);
082      if (! manager.resolveSettings(req.getContext()).isThreadDumpEnabled())
083         throw new Forbidden("The /threaddump endpoint is disabled.  Register a DumpsSettings bean with threadDump enabled to use it.");
084      return manager.threadDump();
085   }
086
087   /**
088    * [GET /heapdump] - HotSpot heap dump (hprof) stream.
089    *
090    * <p>
091    * <b>Deny-by-default:</b> responds {@code 403} unless enabled via {@link DumpsSettings}; {@code 501} on a
092    * JVM without HotSpot heap-dump support.
093    *
094    * @param req The HTTP request.
095    * @param res The HTTP response.
096    * @return A self-deleting stream over the hprof file.
097    * @throws Forbidden If the heap-dump endpoint is not enabled.
098    * @throws NotImplemented If this JVM has no HotSpot heap-dump support.
099    * @throws IOException If the dump could not be produced.
100    */
101   @RestGet(
102      path="/heapdump",
103      summary="Heap dump",
104      description="Produces a HotSpot heap dump (hprof).  Disabled by default; opt in via DumpsSettings."
105   )
106   public InputStream getHeapDump(RestRequest req, RestResponse res) throws IOException {
107      var manager = manager(req);
108      if (! manager.resolveSettings(req.getContext()).isHeapDumpEnabled())
109         throw new Forbidden("The /heapdump endpoint is disabled.  Register a DumpsSettings bean with heapDump enabled to use it.");
110      var stream = manager.heapDumpStream(true);
111      if (stream == null)  // HTT: null only on a non-HotSpot JVM — not reproducible on HotSpot CI.
112         throw new NotImplemented("Heap dumps are not supported on this JVM (no HotSpotDiagnosticMXBean).");
113      res.setHeader("Content-Type", "application/octet-stream");
114      res.setHeader("Content-Disposition", "attachment; filename=\"heapdump.hprof\"");
115      return stream;
116   }
117}