/* * JBoss, Home of Professional Open Source. * Copyright 2016, 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.batch; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; import org.jboss.aesh.console.Config; import org.jboss.as.cli.Attachments; 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.batch.Batch; import org.jboss.as.cli.batch.BatchManager; import org.jboss.as.cli.batch.BatchedCommand; import org.jboss.as.cli.handlers.BaseOperationCommand; import org.jboss.as.cli.handlers.FilenameTabCompleter; import org.jboss.as.cli.impl.ArgumentWithValue; import org.jboss.as.cli.impl.ArgumentWithoutValue; import org.jboss.as.cli.impl.FileSystemPathArgument; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.as.controller.client.OperationMessageHandler; import org.jboss.as.controller.client.OperationResponse; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; /** * * @author Alexey Loubyansky */ public class BatchRunHandler extends BaseOperationCommand { private final ArgumentWithValue file; private final ArgumentWithoutValue verbose; public BatchRunHandler(CommandContext ctx) { super(ctx, "batch-run", true); final FilenameTabCompleter pathCompleter = FilenameTabCompleter.newCompleter(ctx); file = new FileSystemPathArgument(this, pathCompleter, "--file"); verbose = new ArgumentWithoutValue(this, "--verbose", "-v"); } /* (non-Javadoc) * @see org.jboss.as.cli.handlers.CommandHandlerWithHelp#doHandle(org.jboss.as.cli.CommandContext) */ @Override protected void doHandle(CommandContext ctx) throws CommandLineException { final boolean v = verbose.isPresent(ctx.getParsedCommandLine()); final OperationResponse response; boolean failed = false; boolean hasFile = file.getValue(ctx.getParsedCommandLine()) != null; try { final ModelNode request = buildRequest(ctx); OperationBuilder builder = new OperationBuilder(request, true); for (String path : getAttachments(ctx).getAttachedFiles()) { builder.addFileAsAttachment(new File(path)); } final ModelControllerClient client = ctx.getModelControllerClient(); try { response = client.executeOperation(builder.build(), OperationMessageHandler.DISCARD); } catch(Exception e) { throw new CommandFormatException("Failed to perform operation: " + e.getLocalizedMessage()); } if (!Util.isSuccess(response.getResponseNode())) { String msg = formatBatchError(ctx, response.getResponseNode()); if (msg == null) { msg = Util.getFailureDescription(response.getResponseNode()); } throw new CommandFormatException(msg); } ModelNode steps = response.getResponseNode().get(Util.RESULT); if (steps.isDefined()) { // Dispatch to non null response handlers. final Batch batch = ctx.getBatchManager().getActiveBatch(); int i = 1; for (BatchedCommand cmd : batch.getCommands()) { ModelNode step = steps.get("step-" + i); if (step.isDefined()) { if (cmd.getResponseHandler() != null) { cmd.getResponseHandler().handleResponse(step, response); } i += 1; } } } } catch(CommandLineException e) { failed = true; if (hasFile) { throw new CommandLineException("The batch failed with the following error: ", e); } else { throw new CommandLineException("The batch failed with the following error " + "(you are remaining in the batch editing mode to have a chance to correct the error)", e); } } finally { // There is a change in behavior between the file and the added command // With a file, the batch is discarded, whatever the result. if (hasFile) { ctx.getBatchManager().discardActiveBatch(); } else if (!failed) { if (ctx.getBatchManager().isBatchActive()) { ctx.getBatchManager().discardActiveBatch(); } } } if(v) { ctx.printLine(response.getResponseNode().toString()); } else { ctx.printLine("The batch executed successfully"); super.handleResponse(ctx, response.getResponseNode(), true); } } private static String formatBatchError(CommandContext ctx, ModelNode responseNode) { if (responseNode == null) { return null; } ModelNode mn = responseNode.get(Util.RESULT); String msg = null; try { if (mn.isDefined()) { int index = 0; Batch batch = ctx.getBatchManager().getActiveBatch(); StringBuilder b = new StringBuilder(); ModelNode fd = responseNode.get(Util.FAILURE_DESCRIPTION); if (fd.isDefined()) { Property p = fd.asProperty(); b.append(Config.getLineSeparator()). append(p.getName()).append(Config.getLineSeparator()); boolean foundError = false; for (Property prop : mn.asPropertyList()) { ModelNode val = prop.getValue(); if (val.hasDefined(Util.FAILURE_DESCRIPTION)) { b.append("Step: step-").append(index + 1). append(Config.getLineSeparator()); b.append("Operation: ").append(batch.getCommands(). get(index).getCommand()).append(Config.getLineSeparator()); b.append("Failure: ").append(val.get(Util.FAILURE_DESCRIPTION).asString()). append(Config.getLineSeparator()); foundError = true; } index += 1; } if (foundError) { msg = b.toString(); } } } } catch (Exception ex) { // XXX OK, will fallback to null msg. } return msg; } @Override protected ModelNode buildRequestWOValidation(CommandContext ctx) throws CommandFormatException { final String path = file.getValue(ctx.getParsedCommandLine()); final ModelNode headersNode = headers.isPresent(ctx.getParsedCommandLine()) ? headers.toModelNode(ctx) : null; final BatchManager batchManager = ctx.getBatchManager(); if(batchManager.isBatchActive()) { if(path != null) { throw new CommandFormatException("--file is not allowed in the batch mode."); } final Batch batch = batchManager.getActiveBatch(); List<BatchedCommand> currentBatch = batch.getCommands(); if(currentBatch.isEmpty()) { batchManager.discardActiveBatch(); throw new CommandFormatException("The batch is empty."); } final ModelNode request = batch.toRequest(); if(headersNode != null) { request.get(Util.OPERATION_HEADERS).set(headersNode); } return request; } if(path != null) { final File f = new File(path); if(!f.exists()) { throw new CommandFormatException("File " + f.getAbsolutePath() + " does not exist."); } final File currentDir = ctx.getCurrentDir(); final File baseDir = f.getParentFile(); if(baseDir != null) { ctx.setCurrentDir(baseDir); } BufferedReader reader = null; try { reader = Files.newBufferedReader(f.toPath(), StandardCharsets.UTF_8); String line = reader.readLine(); batchManager.activateNewBatch(); while(line != null) { ctx.handle(line); line = reader.readLine(); } final ModelNode request = batchManager.getActiveBatch().toRequest(); if(headersNode != null) { request.get(Util.OPERATION_HEADERS).set(headersNode); } return request; } catch(IOException e) { throw new CommandFormatException("Failed to read file " + f.getAbsolutePath(), e); } catch(CommandLineException e) { throw new CommandFormatException("Failed to create batch from " + f.getAbsolutePath(), e); } finally { if(baseDir != null) { ctx.setCurrentDir(currentDir); } if(reader != null) { try { reader.close(); } catch (IOException e) {} } } } throw new CommandFormatException("Without arguments the command can be executed only in the batch mode."); } @Override public Attachments getAttachments(CommandContext ctx) { final BatchManager batchManager = ctx.getBatchManager(); if (batchManager.isBatchActive()) { final Batch batch = batchManager.getActiveBatch(); return batch.getAttachments(); } return new Attachments(); } @Override protected void handleResponse(CommandContext ctx, ModelNode response, boolean composite) throws CommandLineException { throw new UnsupportedOperationException(); } @Override protected ModelNode buildRequestWithoutHeaders(CommandContext ctx) throws CommandFormatException { throw new UnsupportedOperationException(); } }