/* * 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.trycatch; import java.util.ArrayList; import java.util.List; import org.jboss.as.cli.CommandContext; import org.jboss.as.cli.CommandContext.Scope; import org.jboss.as.cli.CommandLineException; import org.jboss.as.cli.CommandLineRedirection; import org.jboss.as.cli.batch.BatchManager; import org.jboss.as.cli.operation.ParsedCommandLine; import org.jboss.as.cli.parsing.command.CommandFormat; import org.jboss.as.controller.client.ModelControllerClient; /** * * @author Alexey Loubyansky */ class TryCatchFinallyControlFlow implements CommandLineRedirection { private static final String CTX_KEY = "TRY"; private static final int IN_TRY = 0; private static final int IN_CATCH = 1; private static final int IN_FINALLY = 2; static TryCatchFinallyControlFlow get(CommandContext ctx) { return (TryCatchFinallyControlFlow) ctx.get(Scope.CONTEXT, CTX_KEY); } private Registration registration; private List<String> tryList; private List<String> catchList; private List<String> finallyList; private int state; TryCatchFinallyControlFlow(CommandContext ctx) { ctx.set(Scope.CONTEXT, CTX_KEY, this); } @Override public void set(Registration registration) { this.registration = registration; } @Override public void handle(CommandContext ctx) throws CommandLineException { final ParsedCommandLine line = ctx.getParsedCommandLine(); if(line.getFormat() == CommandFormat.INSTANCE) { // let the help through if(line.hasProperty("--help") || line.hasProperty("-h")) { registration.handle(line); return; } final String cmd = line.getOperationName(); if("catch".equals(cmd) || "finally".equals(cmd) || "end-try".equals(cmd)) { registration.handle(line); } else { addLine(line.getOriginalLine()); } } else { addLine(line.getOriginalLine()); } } boolean isInTry() { return state == IN_TRY; } boolean isInFinally() { return state == IN_FINALLY; } void moveToCatch() throws CommandLineException { switch(state) { case IN_TRY: state = IN_CATCH; break; case IN_CATCH: throw new CommandLineException("Already in catch block. Only one catch block is allowed."); case IN_FINALLY: throw new CommandLineException("Catch block is not allowed in finally"); default: throw new IllegalStateException("Unexpected block id: " + state); } } void moveToFinally() throws CommandLineException { switch(state) { case IN_TRY: state = IN_FINALLY; break; case IN_CATCH: state = IN_FINALLY; break; case IN_FINALLY: throw new CommandLineException("Already in finally"); default: throw new IllegalStateException("Unexpected block id: " + state); } } private void addLine(String line) { switch(state) { case IN_TRY: if(tryList == null) { tryList = new ArrayList<String>(); } tryList.add(line); break; case IN_CATCH: if(catchList == null) { catchList = new ArrayList<String>(); } catchList.add(line); break; case IN_FINALLY: if(finallyList == null) { finallyList = new ArrayList<String>(); } finallyList.add(line); break; default: throw new IllegalStateException("Unexpected block id: " + state); } } void run(CommandContext ctx) throws CommandLineException { if(state == IN_TRY) { throw new CommandLineException("The flow can be executed only after catch or finally."); } try { final ModelControllerClient client = ctx.getModelControllerClient(); if (client == null) { throw new CommandLineException("The connection to the controller has not been established."); } registration.unregister(); CommandLineException error = null; if (tryList == null || tryList.isEmpty()) { throw new CommandLineException("The try block is empty"); } try { executeBlock(ctx, tryList, "try"); } catch(CommandLineException eTry) { if(catchList == null) { error = eTry; } else { try { executeBlock(ctx, catchList, "catch"); } catch(CommandLineException eCatch) { error = eCatch; } } } try { executeBlock(ctx, finallyList, "finally"); } catch (CommandLineException eFinally) { // Try or catch exception are hidden by the finally exception. // Make them an exception suppressed by the finally one. // Doing so, they will be part of the error message displayed by // the CLI. if (error != null) { eFinally.addSuppressed(error); } error = eFinally; } if(error != null) { throw error; } } finally { if(registration.isActive()) { registration.unregister(); } ctx.remove(Scope.CONTEXT, CTX_KEY); } } private void executeBlock(CommandContext ctx, List<String> block, String blockName) throws CommandLineException { if(block != null && !block.isEmpty()) { final BatchManager batchManager = ctx.getBatchManager(); // this is to discard a batch started by the block in case the block fails // as the cli remains in the batch mode in case run-batch resulted in an error final boolean discardActivatedBatch = !batchManager.isBatchActive(); try { for (String l : block) { ctx.handle(l); } } finally { if(discardActivatedBatch && batchManager.isBatchActive()) { batchManager.discardActiveBatch(); } } } } }