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}