/* * JBoss, Home of Professional Open Source. * Copyright 2015, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.cli.handlers; import java.io.IOException; import java.util.Collection; import java.util.concurrent.atomic.AtomicReference; import org.jboss.as.cli.CommandContext; import org.jboss.as.cli.CommandFormatException; import org.jboss.as.cli.CommandLineException; import org.jboss.as.cli.Util; import org.jboss.as.cli.accesscontrol.AccessRequirement; import org.jboss.as.cli.accesscontrol.AccessRequirementBuilder; import org.jboss.as.cli.accesscontrol.PerNodeOperationAccess; import org.jboss.as.cli.embedded.EmbeddedProcessLaunch; import org.jboss.as.cli.impl.ArgumentWithValue; import org.jboss.as.cli.impl.AwaiterModelControllerClient; import org.jboss.as.cli.impl.CommaSeparatedCompleter; import org.jboss.as.cli.operation.ParsedCommandLine; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.protocol.StreamUtils; import org.jboss.dmr.ModelNode; /** * @author Alexey Loubyansky * */ public class ShutdownHandler extends BaseOperationCommand { private final ArgumentWithValue restart; private final ArgumentWithValue host; private final ArgumentWithValue timeout; private final AtomicReference<EmbeddedProcessLaunch> embeddedServerRef; private PerNodeOperationAccess hostShutdownPermission; public ShutdownHandler(CommandContext ctx, final AtomicReference<EmbeddedProcessLaunch> embeddedServerRef) { super(ctx, "shutdown", true); this.embeddedServerRef = embeddedServerRef; restart = new ArgumentWithValue(this, SimpleTabCompleter.BOOLEAN, "--restart"); timeout = new ArgumentWithValue(this, "--timeout"); host = new ArgumentWithValue(this, new CommaSeparatedCompleter() { @Override protected Collection<String> getAllCandidates(CommandContext ctx) { return hostShutdownPermission.getAllowedOn(ctx); }} , "--host") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(!ctx.isDomainMode()) { return false; } return super.canAppearNext(ctx); } }; } @Override protected AccessRequirement setupAccessRequirement(CommandContext ctx) { hostShutdownPermission = new PerNodeOperationAccess(ctx, Util.HOST, null, Util.SHUTDOWN); return AccessRequirementBuilder.Factory.create(ctx).any() .operation(Util.SHUTDOWN) .requirement(hostShutdownPermission) .build(); } @Override public boolean isAvailable(CommandContext ctx) { return super.isAvailable(ctx) && ((embeddedServerRef == null || embeddedServerRef.get() == null)); } /* (non-Javadoc) * @see org.jboss.as.cli.handlers.CommandHandlerWithHelp#doHandle(org.jboss.as.cli.CommandContext) */ @Override protected void doHandle(CommandContext ctx) throws CommandLineException { final ModelControllerClient client = ctx.getModelControllerClient(); if(client == null) { throw new CommandLineException("Connection is not available."); } if (embeddedServerRef != null && embeddedServerRef.get() != null) { embeddedServerRef.get().stop(); return; } if (!(client instanceof AwaiterModelControllerClient)) { throw new CommandLineException("Unsupported ModelControllerClient implementation " + client.getClass().getName()); } final AwaiterModelControllerClient cliClient = (AwaiterModelControllerClient) client; final ModelNode op = this.buildRequestWithoutHeaders(ctx); boolean disconnect = true; final String restartValue = restart.getValue(ctx.getParsedCommandLine()); if (Util.TRUE.equals(restartValue) || ctx.isDomainMode() && !isLocalHost(ctx.getModelControllerClient(), host.getValue(ctx.getParsedCommandLine()))) { disconnect = false; } try { final ModelNode response = cliClient.execute(op, true); if(!Util.isSuccess(response)) { throw new CommandLineException(Util.getFailureDescription(response)); } } catch(IOException e) { // if it's not connected, it's assumed the connection has already been shutdown if(cliClient.isConnected()) { StreamUtils.safeClose(client); throw new CommandLineException("Failed to execute :shutdown", e); } } if (disconnect) { ctx.disconnectController(); } else { // if I try to reconnect immediately, it'll hang for 5 sec // which the default connection timeout for model controller client // waiting half a sec on my machine works perfectly try { Thread.sleep(2000); } catch (InterruptedException e) { throw new CommandLineException("Interrupted while pausing before reconnecting.", e); } try { cliClient.ensureConnected(ctx.getConfig().getConnectionTimeout() + 1000); } catch(CommandLineException e) { ctx.disconnectController(); throw e; } } } @Override protected ModelNode buildRequestWithoutHeaders(CommandContext ctx) throws CommandFormatException { final ModelNode op = new ModelNode(); final ParsedCommandLine args = ctx.getParsedCommandLine(); if(ctx.isDomainMode()) { final String hostName = host.getValue(args); if(hostName == null) { throw new CommandFormatException("Missing required argument " + host.getFullName()); } op.get(Util.ADDRESS).add(Util.HOST, hostName); } else { if(host.isPresent(args)) { throw new CommandFormatException(host.getFullName() + " is not allowed in the standalone mode."); } op.get(Util.ADDRESS).setEmptyList(); } op.get(Util.OPERATION).set(Util.SHUTDOWN); setBooleanArgument(args, op, restart, Util.RESTART); setIntArgument(args, op, timeout, Util.TIMEOUT); return op; } protected boolean isLocalHost(ModelControllerClient client, String host) throws CommandLineException { ModelNode request = new ModelNode(); request.get(Util.ADDRESS).setEmptyList(); request.get(Util.OPERATION).set(Util.READ_ATTRIBUTE); request.get(Util.NAME).set(Util.LOCAL_HOST_NAME); ModelNode response; try { response = client.execute(request); } catch (IOException e) { throw new CommandLineException("Failed to read attribute " + Util.LOCAL_HOST_NAME, e); } if(!Util.isSuccess(response)) { throw new CommandLineException("Failed to read attribute " + Util.LOCAL_HOST_NAME + ": " + Util.getFailureDescription(response)); } ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { throw new CommandLineException("The result is not defined for attribute " + Util.LOCAL_HOST_NAME + ": " + result); } return result.asString().equals(host); } protected void setBooleanArgument(final ParsedCommandLine args, final ModelNode op, ArgumentWithValue arg, String paramName) throws CommandFormatException { if(!arg.isPresent(args)) { return; } final String value = arg.getValue(args); if(value == null) { throw new CommandFormatException(arg.getFullName() + " is missing value."); } if(value.equalsIgnoreCase(Util.TRUE)) { op.get(paramName).set(true); } else if(value.equalsIgnoreCase(Util.FALSE)) { op.get(paramName).set(false); } else { throw new CommandFormatException("Invalid value for " + arg.getFullName() + ": '" + value + "'"); } } private void setIntArgument(final ParsedCommandLine args, final ModelNode op, ArgumentWithValue arg, String paramName) throws CommandFormatException { if(!arg.isPresent(args)) { return; } final String value = arg.getValue(args); if(value == null) { throw new CommandFormatException(arg.getFullName() + " is missing value."); } try { Integer i = Integer.parseInt(value); op.get(paramName).set(i); } catch (NumberFormatException nfe) { throw new CommandFormatException("Invalid value for " + arg.getFullName() + ": '" + value + "'"); } } }