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.microservice.examples; 018 019import java.io.*; 020 021import org.apache.juneau.http.classic.response.*; 022import org.apache.juneau.marshall.html.*; 023import org.apache.juneau.microservice.*; 024import org.apache.juneau.microservice.jetty.*; 025import org.apache.juneau.rest.server.*; 026import org.apache.juneau.rest.server.beans.*; 027import org.apache.juneau.rest.server.servlet.*; 028 029/** 030 * Microservice debug utilities. 031 * 032 * <p> 033 * The {@code jetty-thread-dump} navlink is a plain {@code GET /jetty/dump}, which shows the dump. Writing the dump to 034 * <l>jetty-thread-dump.log</l> needs a real {@code POST /jetty/dump}; a link can't reach it, because the router refuses 035 * any override of a {@code GET} to {@code POST}. Put the resource behind a CSRF boundary before mounting it anywhere a 036 * browser can reach. 037 * 038 * <h5 class='section'>See Also:</h5><ul> 039 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauMicroserviceJetty">juneau-microservice-jetty</a> 040 041 * </ul> 042 * 043 * @serial exclude 044 */ 045// @formatter:off 046@Rest( 047 path="/debug", 048 title="Debug", 049 description="Debug Utilities.", 050 allowedMethodParams="OPTIONS" 051) 052@HtmlDocConfig( 053 navlinks={ 054 "up: request:/..", 055 "jetty-thread-dump: servlet:/jetty/dump", 056 "api: servlet:/api", 057 "stats: servlet:/stats" 058 } 059) 060// @formatter:on 061public class DebugResource extends BasicRestServlet { 062 private static final long serialVersionUID = 1L; 063 064 /** 065 * [POST /jetty/dump] - Generates and saves the jetty thread dump file to jetty-thread-dump.log. 066 * 067 * @param req The request. 068 * @param res The response. 069 * @throws Exception Gets converted to 500 response. 070 * @return The thread dump contents. 071 */ 072 @RestPost(path = "/jetty/dump", description = "Generates and saves the jetty thread dump file to jetty-thread-dump.log.") 073 @SuppressWarnings({ 074 "java:S112" // throws Exception intentional - callback/lifecycle method 075 }) 076 public Ok createJettyDump(RestRequest req, RestResponse res) throws Exception { 077 var dump = jettyServerComponent().getServer().dump(); 078 try (var fw = new FileWriter(req.getConfig().get("Logging/logDir").orElse("") + "/jetty-thread-dump.log")) { 079 fw.write(dump); 080 } 081 return Ok.OK; 082 } 083 084 /** 085 * [GET /] - Shows child utilities. 086 * 087 * @return Child utility links. 088 */ 089 @RestGet(path="/", description="Show contents of config file.") 090 public ResourceDescriptions getChildren() { 091 // @formatter:off 092 return ResourceDescriptions 093 .create() 094 .append("jetty/dump", "Jetty thread dump"); 095 // @formatter:on 096 } 097 098 /** 099 * [GET /jetty/dump] - Generates and retrieves the jetty thread dump. 100 * 101 * @param req The request. 102 * @param res The response. 103 * @return The thread dump contents. 104 */ 105 @RestGet(path = "/jetty/dump", description = "Generates and retrieves the jetty thread dump.") 106 public Reader getJettyDump(RestRequest req, RestResponse res) { 107 res.setContentType("text/plain"); 108 return new StringReader(jettyServerComponent().getServer().dump()); 109 } 110 111 @SuppressWarnings({ 112 "resource" // BeanStore returned by Microservice.getBeanStore() is owned by the microservice lifecycle; do not close here. 113 }) 114 private static JettyServerComponent jettyServerComponent() { 115 return Microservice.getInstance().getBeanStore().getBean(JettyServerComponent.class) 116 .orElseThrow(() -> new IllegalStateException("JettyServerComponent bean not found. Did you forget to add JettyConfiguration.class to Microservice.Builder.configurations(...)?")); 117 } 118}