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.ops;
018
019import java.io.*;
020
021import org.apache.juneau.rest.server.*;
022import org.apache.juneau.rest.server.guard.*;
023import org.apache.juneau.rest.server.servlet.*;
024
025/**
026 * Standalone servlet companion of the {@link AdminMixin} mixin.
027 *
028 * <p>
029 * Mounts as a <b>sibling top-level servlet</b> at {@code /admin/*} and serves the same operational
030 * endpoints ({@code /threads}, {@code /heap}, {@code /cache/flush}, {@code /ratelimit}) as the
031 * mixin by delegating to a shared flavor-neutral {@link AdminProvider} worker bean &mdash; so the two forms
032 * cannot drift.
033 *
034 * <p>
035 * Whereas the {@link AdminMixin} mixin pins its ops at {@code /admin/<endpoint>} for
036 * composition into a host at {@code /}, this companion declares the mount at the class level
037 * ({@link Rest#paths() @Rest(paths="/admin/*")}) and pins the ops at the bare endpoint names
038 * (e.g. {@code /threads}) &mdash; eliminating the path-doubling that mixin-shaped ops would produce
039 * when the servlet itself is mounted at {@code /admin/*}.
040 *
041 * <h5 class='section'>Default-deny security posture:</h5>
042 *
043 * <p>
044 * Like the mixin, this servlet is annotated {@link Rest#guards() @Rest(guards=DenyAllGuard.class)},
045 * so every admin path returns {@code 403 Forbidden} until the importer registers a
046 * {@link RestGuardList} factory. Pair the servlet with whatever authentication / authorization
047 * story your service uses.
048 *
049 * <h5 class='figure'>Microservice registration:</h5>
050 *
051 * <p class='bjava'>
052 *    <ja>@Bean</ja> Servlet admin() {
053 *       <jk>return new</jk> AdminServlet();  <jc>// auto-mounted at /admin/* by JettyServerComponent</jc>
054 *    }
055 * </p>
056 *
057 * <h5 class='section'>See Also:</h5><ul>
058 *    <li class='jc'>{@link AdminMixin}
059 *    <li class='jc'>{@link AdminProvider}
060 *    <li class='jc'>{@link DenyAllGuard}
061 *    <li class='jc'>{@link RestGuardList}
062 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerComposition">REST Server — Mixins and Multi-Mount Paths</a>
063 * </ul>
064 *
065 * @serial exclude
066 * @since 10.0.0
067 */
068// @formatter:off
069@Rest(
070   paths="/admin/*",
071   guards=DenyAllGuard.class
072)
073public class AdminServlet extends RestServlet {
074
075   private static final long serialVersionUID = 1L;
076
077   private final transient AdminProvider worker;
078
079   /** No-arg constructor &mdash; uses a default {@link AdminProvider} worker. */
080   public AdminServlet() {
081      this(new AdminProvider());
082   }
083
084   /**
085    * Worker constructor.
086    *
087    * @param worker The shared flavor-neutral admin worker this servlet delegates to. Must not be
088    *    {@code null}.
089    */
090   protected AdminServlet(AdminProvider worker) {
091      this.worker = worker;
092   }
093
094   /**
095    * [GET /threads] &mdash; emit a JSON list of currently-live threads.
096    *
097    * @param res The current REST response.
098    * @throws IOException If an I/O error occurs while writing the response.
099    */
100   @RestGet(
101      path="/threads",
102      summary="Thread dump",
103      description="JSON list of currently-live threads (filtered to exclude framework noise by default).",
104      swagger=@OpSwagger(ignore=true)
105   )
106   public void getThreads(RestResponse res) throws IOException {
107      worker.serveThreads(res);
108   }
109
110   /**
111    * [GET /heap] &mdash; emit JVM heap and non-heap memory statistics.
112    *
113    * @param res The current REST response.
114    * @throws IOException If an I/O error occurs while writing the response.
115    */
116   @RestGet(
117      path="/heap",
118      summary="Heap statistics",
119      description="JVM heap + non-heap memory statistics (Runtime + MemoryMXBean).",
120      swagger=@OpSwagger(ignore=true)
121   )
122   public void getHeap(RestResponse res) throws IOException {
123      worker.serveHeap(res);
124   }
125
126   /**
127    * [POST /cache/flush] &mdash; run all registered cache-flush hooks (or a subset).
128    *
129    * @param req The current REST request &mdash; {@code names} query parameter is read off it.
130    * @param res The current REST response.
131    * @throws IOException If an I/O error occurs while writing the response.
132    */
133   @RestPost(
134      path="/cache/flush",
135      summary="Cache flush",
136      description="Runs the registered cache-flush hooks (all by default; ?names=foo,bar for a subset).",
137      swagger=@OpSwagger(ignore=true)
138   )
139   public void postCacheFlush(RestRequest req, RestResponse res) throws IOException {
140      worker.serveCacheFlush(req, res);
141   }
142
143   /**
144    * [GET /ratelimit] &mdash; emit registered {@link RateLimitGuard} bean configuration.
145    *
146    * @param req The current REST request &mdash; supplies the bean store for guard lookup.
147    * @param res The current REST response.
148    * @throws IOException If an I/O error occurs while writing the response.
149    * @throws NotFound If no {@link RateLimitGuard} bean is registered on the importer's bean
150    *    store.
151    */
152   @RestGet(
153      path="/ratelimit",
154      summary="Rate-limit inspection",
155      description="JSON map of registered RateLimitGuard beans keyed by bean name.",
156      swagger=@OpSwagger(ignore=true)
157   )
158   public void getRateLimit(RestRequest req, RestResponse res) throws IOException {
159      worker.serveRateLimit(req, res);
160   }
161}