/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.system.server; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.eclipse.che.api.core.ConflictException; import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.system.shared.SystemStatus; import org.eclipse.che.api.system.shared.event.SystemStatusChangedEvent; import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; import org.eclipse.che.commons.lang.concurrent.ThreadLocalPropagateContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.inject.Singleton; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import static org.eclipse.che.api.system.shared.SystemStatus.PREPARING_TO_SHUTDOWN; import static org.eclipse.che.api.system.shared.SystemStatus.READY_TO_SHUTDOWN; import static org.eclipse.che.api.system.shared.SystemStatus.RUNNING; /** * Facade for system operations. * * @author Yevhenii Voevodin */ @Singleton public class SystemManager { private static final Logger LOG = LoggerFactory.getLogger(SystemManager.class); private final AtomicReference<SystemStatus> statusRef; private final EventService eventService; private final ServiceTerminator terminator; private final CountDownLatch shutdownLatch = new CountDownLatch(1); @Inject public SystemManager(ServiceTerminator terminator, EventService eventService) { this.terminator = terminator; this.eventService = eventService; this.statusRef = new AtomicReference<>(RUNNING); } /** * Stops some of the system services preparing system to lighter shutdown. * System status is changed from {@link SystemStatus#RUNNING} to * {@link SystemStatus#PREPARING_TO_SHUTDOWN}. * * @throws ConflictException * when system status is different from running */ public void stopServices() throws ConflictException { if (!statusRef.compareAndSet(RUNNING, PREPARING_TO_SHUTDOWN)) { throw new ConflictException("System shutdown has been already called, system status: " + statusRef.get()); } ExecutorService exec = Executors.newSingleThreadExecutor( new ThreadFactoryBuilder().setDaemon(false) .setNameFormat("ShutdownSystemServicesPool") .setUncaughtExceptionHandler(LoggingUncaughtExceptionHandler.getInstance()) .build()); exec.execute(ThreadLocalPropagateContext.wrap(this::doStopServices)); exec.shutdown(); } /** * Gets current system status. * * @see SystemStatus */ public SystemStatus getSystemStatus() { return statusRef.get(); } /** Synchronously stops corresponding services. */ private void doStopServices() { LOG.info("Preparing system to shutdown"); eventService.publish(new SystemStatusChangedEvent(RUNNING, PREPARING_TO_SHUTDOWN)); try { terminator.terminateAll(); statusRef.set(READY_TO_SHUTDOWN); eventService.publish(new SystemStatusChangedEvent(PREPARING_TO_SHUTDOWN, READY_TO_SHUTDOWN)); LOG.info("System is ready to shutdown"); } catch (InterruptedException x) { LOG.error("Interrupted while waiting for system service to shutdown components"); Thread.currentThread().interrupt(); } finally { shutdownLatch.countDown(); } } @PreDestroy @VisibleForTesting void shutdown() throws InterruptedException { if (!statusRef.compareAndSet(RUNNING, PREPARING_TO_SHUTDOWN)) { shutdownLatch.await(); } else { doStopServices(); } } }