/* Copyright 2016 Red Hat, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.jboss.as.protocol.mgmt; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import org.jboss.as.protocol.logging.ProtocolLogger; import org.jboss.remoting3.Channel; import org.jboss.threads.AsyncFuture; import org.jboss.threads.AsyncFutureTask; import org.xnio.Cancellable; /** Standard ActiveOperation implementation */ class ActiveOperationImpl<T, A> extends AsyncFutureTask<T> implements ActiveOperation<T, A> { // All active operations have to use the direct executor for now. At least we need to make sure // completion/cancellation/cleanup are executed before further requests are handled. private static final Executor directExecutor = new Executor() { @Override public void execute(final Runnable command) { command.run(); } }; private static final List<Cancellable> CANCEL_REQUESTED = Collections.emptyList(); private final A attachment; private final Integer operationId; private final ResultHandler<T> resultHandler; private List<Cancellable> cancellables; private volatile Channel channel; ActiveOperationImpl(final Integer operationId, final A attachment, final CompletedCallback<T> callback, final AbstractMessageHandler handler) { super(directExecutor); this.operationId = operationId; this.attachment = attachment; addListener(new Listener<T, Object>() { @Override public void handleComplete(AsyncFuture<? extends T> asyncFuture, Object attachment) { try { callback.completed(asyncFuture.get()); } catch (Exception e) { // } } @Override public void handleFailed(AsyncFuture<? extends T> asyncFuture, Throwable cause, Object attachment) { if(cause instanceof Exception) { callback.failed((Exception) cause); } else { callback.failed(new RuntimeException(cause)); } } @Override public void handleCancelled(AsyncFuture<? extends T> asyncFuture, Object attachment) { handler.removeActiveOperation(operationId); callback.cancelled(); ProtocolLogger.ROOT_LOGGER.debugf("cancelled operation (%d) attachment: (%s) handler: %s.", getOperationId(), getAttachment(), handler); } }, null); this.resultHandler = new ResultHandler<T>() { @Override public boolean done(T result) { try { return ActiveOperationImpl.this.setResult(result); } finally { handler.removeActiveOperation(operationId); } } @Override public boolean failed(Throwable t) { try { boolean failed = ActiveOperationImpl.this.setFailed(t); if(failed) { ProtocolLogger.ROOT_LOGGER.debugf(t, "active-op (%d) failed %s", operationId, attachment); } return failed; } finally { handler.removeActiveOperation(operationId); } } @Override public void cancel() { ProtocolLogger.CONNECTION_LOGGER.debugf("Operation (%d) cancelled", operationId); ActiveOperationImpl.this.cancel(); } }; } @Override public Integer getOperationId() { return operationId; } @Override public ResultHandler<T> getResultHandler() { return resultHandler; } @Override public A getAttachment() { return attachment; } @Override public AsyncFuture<T> getResult() { return this; } @Override public void asyncCancel(boolean interruptionDesired) { final List<Cancellable> cancellables; synchronized (this) { cancellables = this.cancellables; if (cancellables == CANCEL_REQUESTED) { return; } this.cancellables = CANCEL_REQUESTED; if(cancellables == null) { setCancelled(); return; } } for (Cancellable cancellable : cancellables) { cancellable.cancel(); } setCancelled(); } @Override public void addCancellable(final Cancellable cancellable) { // Perhaps just use the IOFuture from XNIO... synchronized (this) { switch (getStatus()) { case CANCELLED: break; case WAITING: final List<Cancellable> cancellables = this.cancellables; if (cancellables == CANCEL_REQUESTED) { break; } else { ((cancellables == null) ? (this.cancellables = new ArrayList<Cancellable>()) : cancellables).add(cancellable); } default: return; } } cancellable.cancel(); } public boolean cancel() { return super.cancel(true); } Channel getChannel() { return channel; } void updateChannelRef(Channel channel) { if (this.channel == null) { this.channel = channel; } } }