/*
* Copyright (c) 2015 Pantheon Technologies s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowjava.protocol.impl.core.connection;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import java.util.function.Function;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessage;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketOutInput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class OutboundQueueEntry {
private static final Logger LOG = LoggerFactory.getLogger(OutboundQueueEntry.class);
public static final Function<OfHeader, Boolean> DEFAULT_IS_COMPLETE = new Function<OfHeader, Boolean>() {
@Override
public Boolean apply(final OfHeader message) {
if (message instanceof MultipartReplyMessage) {
return !((MultipartReplyMessage) message).getFlags().isOFPMPFREQMORE();
}
return true;
}
};
private FutureCallback<OfHeader> callback;
private OfHeader message;
private boolean completed;
private boolean barrier;
private volatile boolean committed;
private OutboundQueueException lastException = null;
private Function<OfHeader, Boolean> isCompletedFunction = DEFAULT_IS_COMPLETE;
void commit(final OfHeader message, final FutureCallback<OfHeader> callback) {
commit(message, callback, DEFAULT_IS_COMPLETE);
}
void commit(final OfHeader message, final FutureCallback<OfHeader> callback,
final Function<OfHeader, Boolean> isCompletedFunction) {
if (this.completed) {
LOG.warn("Can't commit a completed message.");
if (callback != null) {
callback.onFailure(lastException);
}
} else {
this.message = message;
this.callback = callback;
this.barrier = message instanceof BarrierInput;
this.isCompletedFunction = isCompletedFunction;
// Volatile write, needs to be last
this.committed = true;
}
}
void reset() {
barrier = false;
callback = null;
completed = false;
message = null;
// Volatile write, needs to be last
committed = false;
}
boolean isBarrier() {
return barrier;
}
boolean isCommitted() {
return committed;
}
boolean isCompleted() {
return completed;
}
OfHeader takeMessage() {
final OfHeader ret = message;
if (!barrier) {
checkCompletionNeed();
}
message = null;
return ret;
}
private void checkCompletionNeed() {
if (callback == null || (message instanceof PacketOutInput)) {
completed = true;
if (callback != null) {
callback.onSuccess(null);
callback = null;
}
committed = false;
}
}
boolean complete(final OfHeader response) {
Preconditions.checkState(!completed, "Attempted to complete a completed message with response %s", response);
// Multipart requests are special, we have to look at them to see
// if there is something outstanding and adjust ourselves accordingly
final boolean reallyComplete = isCompletedFunction.apply(response);
completed = reallyComplete;
if (callback != null) {
callback.onSuccess(response);
if (reallyComplete) {
// We will not need the callback anymore, make sure it can be GC'd
callback = null;
}
}
LOG.debug("Entry {} completed {} with response {}", this, completed, response);
return reallyComplete;
}
void fail(final OutboundQueueException cause) {
if (!completed) {
lastException = cause;
completed = true;
if (callback != null) {
callback.onFailure(cause);
callback = null;
}
} else {
LOG.warn("Ignoring failure {} for completed message", cause);
}
}
@VisibleForTesting
/** This method is only for testing to prove that after queue entry is completed there is not callback future */
boolean hasCallback() {
return (callback != null);
}
}