package eu.openaire.urls_controller.controllers; import eu.openaire.urls_controller.models.WorkerInfo; import eu.openaire.urls_controller.services.ShutdownService; import eu.openaire.urls_controller.util.GenericUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping("") public class ShutdownController { private static final Logger logger = LoggerFactory.getLogger(ShutdownController.class); @Autowired ShutdownService shutdownService; public static boolean shouldShutdownService = false; public static boolean shouldShutdownAllWorkers = false; @PostMapping("shutdownService") public ResponseEntity shutdownServiceGracefully(HttpServletRequest request) { String initMsg = "Received a \"shutdownService\" request "; String remoteAddr = GenericUtils.getRequestorAddress(request); initMsg += "from [" + remoteAddr + "]. "; ResponseEntity responseEntity = shutdownService.passSecurityChecks(remoteAddr, initMsg); if ( responseEntity != null ) return responseEntity; String endingMsg; if ( shouldShutdownService ) { endingMsg = "The controller has already received a \"shutdownService\" request (which was not canceled afterwards)."; logger.info(initMsg + endingMsg); } else { shouldShutdownService = true; endingMsg = "The service will shutdown, after finishing current work."; logger.info(initMsg + endingMsg); shutdownService.postShutdownOrCancelRequestsToAllWorkers(false); // That's it for now. The workers may take some hours to finish their work (including delivering the full-text files). // A scheduler monitors the shutdown of the workers. Once all worker have shutdown, the Controller shuts down as well. } return ResponseEntity.ok().body(endingMsg + GenericUtils.endOfLine); } @PostMapping("cancelShutdownService") public ResponseEntity cancelShutdownServiceGracefully(HttpServletRequest request) { String initMsg = "Received a \"cancelShutdownService\" request "; String remoteAddr = GenericUtils.getRequestorAddress(request); initMsg += "from [" + remoteAddr + "]. "; ResponseEntity responseEntity = shutdownService.passSecurityChecks(remoteAddr, initMsg); if ( responseEntity != null ) return responseEntity; shouldShutdownService = false; String endingMsg = "Any previous \"shutdownService\"-request is canceled."; logger.info(initMsg + endingMsg); // Cancel the shutdown of the workers, if we are able to catch up with them before they have already shutdown.. shutdownService.postShutdownOrCancelRequestsToAllWorkers(true); return ResponseEntity.ok().body(endingMsg + GenericUtils.endOfLine); } // The "shutdownAllWorkers" and a "cancelShutdownAllWorkers" endpoints help when updating only the workers, // while keeping the Controller running and accepting bulk-import requests. @PostMapping("shutdownAllWorkers") public ResponseEntity shutdownAllWorkersGracefully(HttpServletRequest request) { String initMsg = "Received a \"shutdownAllWorkers\" request "; String remoteAddr = GenericUtils.getRequestorAddress(request); initMsg += "from [" + remoteAddr + "]. "; ResponseEntity responseEntity = shutdownService.passSecurityChecks(remoteAddr, initMsg); if ( responseEntity != null ) return responseEntity; String endingMsg; if ( shouldShutdownAllWorkers ) { endingMsg = "The controller has already received a \"shutdownAllWorkers\" request (which was not canceled afterwards)."; logger.info(initMsg + endingMsg); } else { shouldShutdownAllWorkers = true; endingMsg = "All workers will shutdown, after finishing current work."; logger.info(initMsg + endingMsg); shutdownService.postShutdownOrCancelRequestsToAllWorkers(false); // That's it for now. The workers may take some hours to finish their work (including delivering the full-text files). // The service will continue to run and handle bulk-import requests. // Once the workers are ready to work again, they can be started without any additional configuration. } return ResponseEntity.ok().body(endingMsg + GenericUtils.endOfLine); } @PostMapping("cancelShutdownAllWorkers") public ResponseEntity cancelShutdownAllWorkersGracefully(HttpServletRequest request) { String initMsg = "Received a \"cancelShutdownAllWorkers\" request "; String remoteAddr = GenericUtils.getRequestorAddress(request); initMsg += "from [" + remoteAddr + "]. "; ResponseEntity responseEntity = shutdownService.passSecurityChecks(remoteAddr, initMsg); if ( responseEntity != null ) return responseEntity; shouldShutdownAllWorkers = false; String endingMsg = "Any previous \"shutdownAllWorkers\"-request is canceled."; logger.info(initMsg + endingMsg); // Cancel the shutdown of the workers, if we are able to catch up with them before they have already shutdown.. shutdownService.postShutdownOrCancelRequestsToAllWorkers(true); return ResponseEntity.ok().body(endingMsg + GenericUtils.endOfLine); } @PostMapping("workerShutdownReport") public ResponseEntity workerShutdownReport(@RequestParam String workerId, HttpServletRequest request) { String remoteAddr = GenericUtils.getRequestorAddress(request); String initMsg = "Received a \"workerShutdownReport\" from worker: \"" + workerId + "\" [IP: " + remoteAddr + "]."; WorkerInfo workerInfo = UrlsController.workersInfoMap.get(workerId); if ( workerInfo == null ) { String errorMsg = "The worker with id \"" + workerId + "\" has not participated in the PDF-Aggregation-Service!"; logger.warn(initMsg + GenericUtils.endOfLine + errorMsg); return ResponseEntity.badRequest().body(errorMsg); } if ( ! remoteAddr.equals(workerInfo.getWorkerIP()) ) { logger.error(initMsg + " The request came from an IP different from the one this worker was registered with: " + workerInfo.getWorkerIP()); return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } logger.info(initMsg); workerInfo.setHasShutdown(true); // This will update the map. UrlsController.numOfActiveWorkers.decrementAndGet(); // Return "HTTP-OK" to this worker. If this was part of a shutdown-service request, then wait for the scheduler to check and shutdown the service. return ResponseEntity.ok().build(); } }