/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.controller.remote; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION; import java.util.Collection; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.jboss.as.controller.client.OperationResponse; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.dmr.ModelNode; import org.jboss.threads.AsyncFuture; /** * Basic operation listener backed by a blocking queue. If the limit of the queue is reached prepared operations * are going to be rolled back automatically. * * @param <T> the operation type */ public class BlockingQueueOperationListener<T extends TransactionalProtocolClient.Operation> implements TransactionalProtocolClient.TransactionalOperationListener<T> { private final BlockingQueue<TransactionalProtocolClient.PreparedOperation<T>> queue; public BlockingQueueOperationListener() { this(new LinkedBlockingQueue<TransactionalProtocolClient.PreparedOperation<T>>()); } public BlockingQueueOperationListener(final int capacity) { this(new ArrayBlockingQueue<TransactionalProtocolClient.PreparedOperation<T>>(capacity)); } public BlockingQueueOperationListener(final BlockingQueue<TransactionalProtocolClient.PreparedOperation<T>> queue) { this.queue = queue; } @Override public void operationPrepared(final TransactionalProtocolClient.PreparedOperation<T> prepared) { ControllerLogger.MGMT_OP_LOGGER.tracef("received prepared operation %s", prepared); if(! queue.offer(prepared)) { prepared.rollback(); } } @Override public void operationFailed(T operation, ModelNode result) { ControllerLogger.MGMT_OP_LOGGER.tracef("received failed operation %s", operation); queue.offer(new FailedOperation<T>(operation, result)); } @Override public void operationComplete(T operation, OperationResponse result) { // } /** * Retrieves and removes the head of the underlying queue, waiting if necessary until an element becomes available. * * @return the prepared operation * @throws InterruptedException */ public TransactionalProtocolClient.PreparedOperation<T> retrievePreparedOperation() throws InterruptedException { return queue.take(); } protected void drainTo(final Collection<TransactionalProtocolClient.PreparedOperation<T>> collection) { if(queue.size() > 0) { queue.drainTo(collection); } } /** * Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an element to become available. * * @param timeout the timeout * @param timeUnit the time unit * @return the prepared operation * @throws InterruptedException */ public TransactionalProtocolClient.PreparedOperation<T> retrievePreparedOperation(final long timeout, final TimeUnit timeUnit) throws InterruptedException { return queue.poll(timeout, timeUnit); } public static class FailedOperation<T extends TransactionalProtocolClient.Operation> implements TransactionalProtocolClient.PreparedOperation<T> { private final T operation; private final ModelNode finalResult; private final boolean timedOut; /** * Create a failed operation. * * @param operation the operation * @param t the throwable * @param <T> the operation type * @return the failed operation */ public static <T extends TransactionalProtocolClient.Operation> TransactionalProtocolClient.PreparedOperation<T> create(final T operation, final Throwable t) { final String failureDescription = t.getLocalizedMessage() == null ? t.getClass().getName() : t.getLocalizedMessage(); return create(operation, failureDescription); } /** * Create a failed operation. * * @param operation the operation * @param failureDescription the failure description * @param <T> the operation type * @return the failed operation */ public static <T extends TransactionalProtocolClient.Operation> TransactionalProtocolClient.PreparedOperation<T> create(final T operation, final String failureDescription) { final ModelNode failedResult = new ModelNode(); failedResult.get(ModelDescriptionConstants.OUTCOME).set(ModelDescriptionConstants.FAILED); failedResult.get(FAILURE_DESCRIPTION).set(failureDescription); return new FailedOperation<T>(operation, failedResult, false); } private static boolean isTimeoutFailureDescription(final ModelNode response) { boolean result = response.hasDefined(FAILURE_DESCRIPTION); if (result) { result = response.get(FAILURE_DESCRIPTION).asString().startsWith("WFLYCTL0409"); } return result; } public FailedOperation(final T operation, final ModelNode finalResult) { this(operation, finalResult, isTimeoutFailureDescription(finalResult)); } public FailedOperation(final T operation, final ModelNode finalResult, final boolean timedOut) { this.operation = operation; this.finalResult = finalResult; this.timedOut = timedOut; } @Override public T getOperation() { return operation; } @Override public ModelNode getPreparedResult() { return finalResult; } @Override public boolean isDone() { return true; } @Override public boolean isFailed() { return true; } @Override public boolean isTimedOut() { return timedOut; } @Override public AsyncFuture<OperationResponse> getFinalResult() { return new CompletedFuture<>(OperationResponse.Factory.createSimple(finalResult)); } @Override public void commit() { throw new IllegalStateException(); } @Override public void rollback() { throw new IllegalStateException(); } } }