package org.dcache.webadmin.model.dataaccess.communication.impl; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.concurrent.CountDownLatch; import diskCacheV111.vehicles.Message; import dmg.cells.nucleus.CellPath; import org.dcache.cells.CellStub; import org.dcache.webadmin.model.dataaccess.communication.CellMessageGenerator; import org.dcache.webadmin.model.dataaccess.communication.CellMessageGenerator.CellMessageRequest; import org.dcache.webadmin.model.dataaccess.communication.CommandSender; /** * Command Sender to send commands to dCache-Cells via cell-communication. * It does this with an implementation * of a callback, to be able to send multiple commands asynchronously. * @author jans */ public class CellCommandSender implements CommandSender { private CellMessageGenerator<?> _messageGenerator; private CellStub _cellStub; private CountDownLatch _doneSignal; private boolean _allSuccessful = true; private static final Logger _log = LoggerFactory.getLogger(CellCommandSender.class); public CellCommandSender(CellMessageGenerator<?> messageGenerator) { _messageGenerator = messageGenerator; _doneSignal = new CountDownLatch(messageGenerator.getNumberOfMessages()); } @Override public void sendAndWait() throws InterruptedException { sendMessages(); _doneSignal.await(); } private void sendMessages() { for (CellMessageRequest<? extends Serializable> messageRequest : _messageGenerator) { CellMessageCallback callback = new CellMessageCallback(messageRequest); _log.debug("sending to: {}", messageRequest.getDestination()); Serializable message = messageRequest.getPayload(); CellPath destination = messageRequest.getDestination(); Class<? extends Serializable> payloadType = messageRequest.getPayloadType(); ListenableFuture<? extends Serializable> future = _cellStub.send(destination, message, payloadType); Futures.addCallback(future, callback, MoreExecutors.directExecutor()); } _log.debug("messages send"); } public void setCellStub(CellStub cellStub) { _cellStub = cellStub; } @Override public boolean allSuccessful() { return _allSuccessful; } private void processFailure() { _log.debug("failure {}", System.currentTimeMillis()); _allSuccessful = false; } private void processAnswered() { _doneSignal.countDown(); } /** * Callback to handle answer of a Cell to a Message. * @author jans */ private class CellMessageCallback implements FutureCallback<Serializable> { private CellMessageRequest<? extends Serializable> _messageRequest; public CellMessageCallback(CellMessageRequest<? extends Serializable> messageRequest) { _messageRequest = messageRequest; // considered sending as not successful until replied _messageRequest.setSuccessful(false); } @Override public void onSuccess(Serializable message) { if (message instanceof Message && ((Message) message).getReturnCode() != 0) { processFailure(); } else { _messageRequest.setSuccessful(true); } _messageRequest.setAnswer(message); setAnswered(); } @Override public void onFailure(Throwable error) { _log.debug("error object: {}", error.toString()); processFailure(); setAnswered(); } private void setAnswered() { _log.debug("{} answered {}", _messageRequest.getDestination(), System.currentTimeMillis()); processAnswered(); } } }