All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.plugin.tools.server.AbstractServerManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright The WildFly Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.wildfly.plugin.tools.server;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.helpers.Operations;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.wildfly.plugin.tools.ContainerDescription;
import org.wildfly.plugin.tools.DeploymentManager;
import org.wildfly.plugin.tools.OperationExecutionException;

/**
 * A utility for validating a server installations and executing common operations.
 *
 * @author James R. Perkins
 */
@SuppressWarnings("unused")
abstract class AbstractServerManager implements ServerManager {
    private static final Logger LOGGER = Logger.getLogger(AbstractServerManager.class);

    protected final ProcessHandle process;
    final T client;
    private final boolean shutdownOnClose;
    private final DeploymentManager deploymentManager;
    private final AtomicBoolean closed;

    protected AbstractServerManager(final ProcessHandle process, final T client,
            final boolean shutdownOnClose) {
        this.process = process;
        this.client = client;
        this.shutdownOnClose = shutdownOnClose;
        deploymentManager = DeploymentManager.create(client);
        this.closed = new AtomicBoolean(false);
    }

    @Override
    public ModelControllerClient client() {
        checkState();
        return client;
    }

    @Override
    public ContainerDescription containerDescription() throws IOException {
        return ContainerDescription.lookup(client());
    }

    @Override
    public DeploymentManager deploymentManager() {
        return deploymentManager;
    }

    /**
     * Determines the servers "launch-type".
     *
     * @return the servers launch-type or "unknown" if it could not be determined
     */
    @Override
    public String launchType() {
        return ServerManager.launchType(client()).orElse("unknown");
    }

    @Override
    public CompletableFuture kill() {
        final CompletableFuture cf = new CompletableFuture<>();
        if (process != null && process.isAlive()) {
            internalClose(false, false);
            if (process.destroyForcibly()) {
                cf.thenCombine(process.onExit(), (serverManager, processHandle) -> serverManager);
            }
        } else {
            cf.complete(this);
        }
        return cf;
    }

    @Override
    public boolean waitFor(final long startupTimeout, final TimeUnit unit) throws InterruptedException {
        long timeout = unit.toMillis(startupTimeout);
        final long sleep = 100;
        while (timeout > 0) {
            long before = System.currentTimeMillis();
            if (isRunning()) {
                break;
            }
            timeout -= (System.currentTimeMillis() - before);
            if (process != null && !process.isAlive()) {
                throw new RuntimeException(
                        String.format("The process %d is no longer active.", process.pid()));
            }
            TimeUnit.MILLISECONDS.sleep(sleep);
            timeout -= sleep;
        }
        if (timeout <= 0) {
            if (process != null) {
                process.destroy();
            }
            return false;
        }
        return true;
    }

    /**
     * Takes a snapshot of the current servers configuration and returns the relative file name of the snapshot.
     * 

* This simply executes the {@code take-snapshot} operation with no arguments. *

* * @return the file name of the snapshot configuration file * * @throws IOException if an error occurs executing the operation * @throws OperationExecutionException if the take-snapshot operation fails */ @Override public String takeSnapshot() throws IOException, OperationExecutionException { final ModelNode op = Operations.createOperation("take-snapshot"); final String snapshot = executeOperation(op).asString(); return snapshot.contains(File.separator) ? snapshot.substring(snapshot.lastIndexOf(File.separator) + 1) : snapshot; } /** * Reloads the server and returns immediately. * * @param reloadOp the reload operation to execute * * @throws OperationExecutionException if the reload operation fails */ @Override public void executeReload(final ModelNode reloadOp) throws IOException, OperationExecutionException { try { executeOperation(reloadOp); } catch (IOException e) { final Throwable cause = e.getCause(); if (!(cause instanceof ExecutionException) && !(cause instanceof CancellationException)) { throw e; } // else ignore, this might happen if the channel gets closed before we got the response } } @Override public void shutdown(final long timeout) throws IOException { checkState(); internalShutdown(client(), timeout); waitForShutdown(client()); } @Override public CompletableFuture shutdownAsync(final long timeout) { checkState(); final ServerManager serverManager = this; if (process != null) { return CompletableFuture.supplyAsync(() -> { try { internalShutdown(client, timeout); } catch (IOException e) { throw new CompletionException("Failed to shutdown server.", e); } return null; }) .thenCombine(process.onExit(), (outcome, processHandle) -> null) .handle((ignore, error) -> { if (error != null && process.isAlive()) { if (process.destroyForcibly()) { LOGGER.warnf(error, "Failed to shutdown the server. An attempt to destroy the process %d has been made, but it may still temporarily run in the background.", process.pid()); } else { LOGGER.warnf(error, "Failed to shutdown server and destroy the process %d. The server may still be running in a process.", process.pid()); } } return serverManager; }); } return CompletableFuture.supplyAsync(() -> { try { internalShutdown(client, timeout); } catch (IOException e) { throw new CompletionException("Failed to shutdown server.", e); } return null; }).thenApply(outcome -> { // Wait for the server manager to finish shutting down while (ServerManager.isRunning(client)) { Thread.onSpinWait(); } return serverManager; }); } @Override public boolean isClosed() { return closed.get(); } @Override public void close() { internalClose(shutdownOnClose, true); } void checkState() { if (closed.get()) { throw new IllegalStateException("The server manager has been closed and cannot process requests"); } } void internalClose(final boolean shutdownOnClose, final boolean waitForShutdown) { if (closed.compareAndSet(false, true)) { if (shutdownOnClose) { try { internalShutdown(client, 0); if (waitForShutdown) { waitForShutdown(client); } } catch (IOException e) { LOGGER.error("Failed to shutdown the server while closing the server manager.", e); } } try { client.close(); } catch (IOException e) { LOGGER.error("Failed to close the client.", e); } } } abstract void internalShutdown(ModelControllerClient client, long timeout) throws IOException; private void waitForShutdown(final ModelControllerClient client) { if (process != null) { try { process.onExit() .get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(String.format("Error waiting for process %d to exit.", process.pid()), e); } } else { // Wait for the server manager to finish shutting down while (ServerManager.isRunning(client)) { Thread.onSpinWait(); } } } /** * Executes the operation with the {@code client} returning the result or throwing an {@link OperationExecutionException} * if the operation failed. * * @param client the client used to execute the operation * @param op the operation to execute * * @return the result of the operation * * @throws IOException if an error occurs communicating with the server * @throws OperationExecutionException if the operation failed */ static ModelNode executeOperation(final ModelControllerClient client, final ModelNode op) throws IOException, OperationExecutionException { final ModelNode result = client.execute(op); if (Operations.isSuccessfulOutcome(result)) { return Operations.readResult(result); } throw new OperationExecutionException(op, result); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy