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 static org.apache.juneau.commons.utils.Shorts.*; 020 021import org.apache.juneau.rest.server.*; 022import org.apache.juneau.rest.server.servlet.*; 023 024/** 025 * Provides the capability to shut down this REST microservice through a REST call. 026 * 027 * <p> 028 * {@code POST /shutdown} stops the process: the {@code "OK"} response is sent first, and the exit follows about a 029 * second later on a background thread. Shutting down is a write, so it is a {@code POST}, never a {@code GET}: a safe 030 * method passes every CSRF check, so a {@code GET} would let any page that embeds {@code <img src=".../shutdown">} 031 * stop the process. {@code GET /shutdown} is answered with {@code 405}. 032 * 033 * <p> 034 * From the command line, send a non-form content type so a CSRF boundary in front of this resource (see 035 * {@link AdminBoundary}) admits the request without a token: 036 * <p class='bcode'> 037 * curl -X POST -H 'Content-Type: application/json' http://localhost:10000/shutdown 038 * </p> 039 * 040 * <p> 041 * This resource has no authentication of its own. Do not expose it on an untrusted network. 042 * 043 * <h5 class='section'>See Also:</h5><ul> 044 * <li class='jc'>{@link AdminBoundary} 045 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauMicroservice">juneau-microservice</a> 046 * </ul> 047 * 048 * @serial exclude 049 */ 050@Rest(path = "/shutdown", title = "Shut down this resource") 051public class ShutdownResource extends BasicRestServlet { 052 053 /** 054 * How the process is stopped. 055 * 056 * <p> 057 * The default calls {@link System#exit(int)}. Pass another to {@link ShutdownResource#ShutdownResource(Exit)}, or 058 * register one as a bean in its parent resource's bean store, to observe a shutdown without stopping the JVM, for 059 * example in a test. 060 */ 061 @FunctionalInterface 062 public interface Exit { 063 064 /** 065 * Stops the process. 066 * 067 * @param status The exit status. 068 */ 069 void exit(int status); 070 } 071 072 private static final long serialVersionUID = 1L; 073 074 // Not serialized: a servlet is never restored from a stream, and an exit is behaviour, not state. 075 private final transient Exit exit; 076 077 /** 078 * Constructor. 079 * 080 * <p> 081 * A shutdown calls {@link System#exit(int)}. 082 */ 083 public ShutdownResource() { 084 this(System::exit); 085 } 086 087 /** 088 * Constructor. 089 * 090 * <p> 091 * When the resource is mounted as a child by class, this constructor is used if the parent resource's bean store 092 * holds an {@link Exit}, for example from a <c>@Bean</c> method on the parent. A microservice's own beans are not 093 * in that store. 094 * 095 * @param exit How a shutdown stops the process. Must not be <jk>null</jk>. 096 * @throws IllegalArgumentException If {@code exit} is <jk>null</jk>. 097 */ 098 public ShutdownResource(Exit exit) { 099 this.exit = reqnn("exit", exit); 100 } 101 102 /** 103 * [POST /] - Shuts down this microservice. 104 * 105 * @return The string <js>"OK"</js>. 106 */ 107 @Mutating("Stops the process") 108 @RestPost(path = "/", summary = "Shut down this microservice", description = "Stops the process about a second after the response is sent.") 109 @SuppressWarnings({ 110 "java:S2654" // Background thread is required so the delayed exit runs off the request thread, letting this method's "OK" response flush to the client before the JVM dies; no managed executor is available in this embedded microservice container. 111 }) 112 public String shutdown() { 113 var e = exit; 114 new Thread(() -> { 115 try { 116 Thread.sleep(1000); 117 e.exit(0); 118 } catch (@SuppressWarnings("unused") InterruptedException e2) { 119 Thread.currentThread().interrupt(); 120 } 121 }).start(); 122 return "OK"; 123 } 124}