/*
* Copyright 2012 Thomas Bocek
*
* 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 net.tomp2p.p2p.builder;
import io.netty.buffer.ByteBuf;
import java.security.KeyPair;
import net.tomp2p.connection.ConnectionBean;
import net.tomp2p.connection.ConnectionConfiguration;
import net.tomp2p.connection.PeerConnection;
import net.tomp2p.connection.RequestHandler;
import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.FutureChannelCreator;
import net.tomp2p.futures.FutureDirect;
import net.tomp2p.futures.FutureDone;
import net.tomp2p.futures.FuturePeerConnection;
import net.tomp2p.message.Message;
import net.tomp2p.p2p.Peer;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.rpc.SendDirectBuilderI;
import net.tomp2p.utils.Utils;
public class SendDirectBuilder implements ConnectionConfiguration, SendDirectBuilderI,
SignatureBuilder<SendDirectBuilder> {
private static final FutureDirect FUTURE_REQUEST_SHUTDOWN = new FutureDirect(null, false).failed("Peer is shutting down.");
private final Peer peer;
private final PeerAddress recipientAddress;
private ByteBuf dataBuffer;
private FuturePeerConnection recipientConnection;
private PeerConnection peerConnection;
private Object object;
private boolean streaming = false;
private boolean forceUDP = false;
private KeyPair keyPair = null;
private int idleTCPMillis = ConnectionBean.DEFAULT_TCP_IDLE_MILLIS;
private int idleUDPMillis = ConnectionBean.DEFAULT_UDP_IDLE_MILLIS;
private int connectionTimeoutTCPMillis = ConnectionBean.DEFAULT_CONNECTION_TIMEOUT_TCP;
private int heartBeatSeconds = ConnectionBean.DEFAULT_HEARTBEAT_SECONDS;
private boolean forceTCP = false;
public SendDirectBuilder(Peer peer, PeerAddress recipientAddress) {
this.peer = peer;
this.recipientAddress = recipientAddress;
this.recipientConnection = null;
}
public SendDirectBuilder(Peer peer, FuturePeerConnection recipientConnection) {
this.peer = peer;
this.recipientAddress = null;
this.recipientConnection = recipientConnection;
}
public SendDirectBuilder(Peer peer, PeerConnection peerConnection) {
this.peer = peer;
this.recipientAddress = null;
this.peerConnection = peerConnection;
}
public PeerAddress recipient() {
return recipientAddress;
}
public ByteBuf dataBuffer() {
return dataBuffer;
}
public SendDirectBuilder dataBuffer(ByteBuf dataBuffer) {
this.dataBuffer = dataBuffer;
return this;
}
public FuturePeerConnection connection() {
return recipientConnection;
}
public SendDirectBuilder connection(FuturePeerConnection connection) {
this.recipientConnection = connection;
return this;
}
public PeerConnection peerConnection() {
return peerConnection;
}
public SendDirectBuilder peerConnection(PeerConnection peerConnection) {
this.peerConnection = peerConnection;
return this;
}
public Object object() {
return object;
}
public SendDirectBuilder object(Object object) {
this.object = object;
return this;
}
public SendDirectBuilder streaming(boolean streaming) {
this.streaming = streaming;
return this;
}
public boolean isStreaming() {
return streaming;
}
public SendDirectBuilder streaming() {
this.streaming = true;
return this;
}
public boolean isRaw() {
return object == null;
}
public FutureDirect start() {
if (peer.isShutdown()) {
return FUTURE_REQUEST_SHUTDOWN;
}
final boolean keepAlive;
final PeerAddress remotePeer;
if (recipientAddress != null && recipientConnection == null) {
keepAlive = false;
remotePeer = recipientAddress;
} else if (recipientAddress == null && recipientConnection != null) {
keepAlive = true;
remotePeer = recipientConnection.attachment();
} else if (peerConnection != null) {
keepAlive = true;
remotePeer = peerConnection.remotePeer();
} else {
throw new IllegalArgumentException("Either the recipient address or peer connection has to be set.");
}
Message message = peer.directDataRPC().sendInternal0(remotePeer, this);
final FutureDirect futureResponse = new FutureDirect(message, isRaw());
futureResponse.request().keepAlive(keepAlive);
final RequestHandler request = peer.directDataRPC().sendInternal(futureResponse, this);
if (keepAlive) {
if (peerConnection != null) {
sendDirectRequest(request, peerConnection);
} else {
recipientConnection.addListener(new BaseFutureAdapter<FuturePeerConnection>() {
@Override
public void operationComplete(final FuturePeerConnection future) throws Exception {
if (future.isSuccess()) {
sendDirectRequest(request, future.object());
} else {
request.futureResponse().failed("Could not acquire channel (1).", future);
}
}
});
}
} else {
FutureChannelCreator futureChannelCreator = peer.connectionBean().reservation()
.create(isForceUDP() ? 1 : 0, isForceUDP() ? 0 : 1);
Utils.addReleaseListener(futureChannelCreator, request.futureResponse());
futureChannelCreator.addListener(new BaseFutureAdapter<FutureChannelCreator>() {
@Override
public void operationComplete(final FutureChannelCreator future) throws Exception {
if (future.isSuccess()) {
if (isForceUDP()) {
request.sendUDP(future.channelCreator());
} else {
request.sendTCP(future.channelCreator());
}
} else {
request.futureResponse().failed("Could not create channel.", future);
}
}
});
}
return futureResponse;
}
private static void sendDirectRequest(final RequestHandler request, final PeerConnection peerConnection) {
//if we reuse the same peerconnetion, send data sequentially
synchronized(peerConnection) {
request.sendTCP(peerConnection);
}
}
public boolean isForceUDP() {
return forceUDP;
}
public SendDirectBuilder forceUDP(final boolean forceUDP) {
this.forceUDP = forceUDP;
return this;
}
public SendDirectBuilder forceUDP() {
this.forceUDP = true;
return this;
}
/*
* (non-Javadoc)
*
* @see net.tomp2p.p2p.builder.ConnectionConfiguration#idleTCPSeconds()
*/
@Override
public int idleTCPMillis() {
return idleTCPMillis;
}
/**
* @param idleTCPSeconds
* The time that a connection can be idle before its considered
* not active for short-lived connections
* @return This class
*/
public SendDirectBuilder idleTCPMillis(final int idleTCPMillis) {
this.idleTCPMillis = idleTCPMillis;
return this;
}
/*
* (non-Javadoc)
*
* @see net.tomp2p.p2p.builder.ConnectionConfiguration#idleUDPSeconds()
*/
@Override
public int idleUDPMillis() {
return idleUDPMillis;
}
/**
* @param idleUDPSeconds
* The time that a connection can be idle before its considered
* not active for short-lived connections
* @return This class
*/
public SendDirectBuilder idleUDPMillis(final int idleUDPMillis) {
this.idleUDPMillis = idleUDPMillis;
return this;
}
/**
* @param connectionTimeoutTCPMillis
* The time a TCP connection is allowed to be established
* @return This class
*/
public SendDirectBuilder connectionTimeoutTCPMillis(final int connectionTimeoutTCPMillis) {
this.connectionTimeoutTCPMillis = connectionTimeoutTCPMillis;
return this;
}
/*
* (non-Javadoc)
*
* @see
* net.tomp2p.p2p.builder.ConnectionConfiguration#connectionTimeoutTCPMillis
* ()
*/
@Override
public int connectionTimeoutTCPMillis() {
return connectionTimeoutTCPMillis;
}
/**
* @return Set to true if the communication should be TCP, default is UDP
* for routing
*/
public boolean isForceTCP() {
return forceTCP;
}
/**
* @param forceTCP
* Set to true if the communication should be TCP, default is UDP
* for routing
* @return This class
*/
public SendDirectBuilder forceTCP(final boolean forceTCP) {
this.forceTCP = forceTCP;
return this;
}
/**
* @return Set to true if the communication should be TCP, default is UDP
* for routing
*/
public SendDirectBuilder forceTCP() {
this.forceTCP = true;
return this;
}
@Override
public int heartBeatSeconds() {
return heartBeatSeconds;
}
/**
* @param slowResponseTimeoutSeconds the amount of seconds a requester waits for the final answer of a
* slow peer. If the slow peer does not answer within this time, the request fails.
* @return This class
*/
public SendDirectBuilder heartBeatSeconds(final int heartBeatSeconds) {
this.heartBeatSeconds = heartBeatSeconds;
return this;
}
/**
* @return Set to true if the message should be signed. For protecting an
* entry, this needs to be set to true.
*/
public boolean isSign() {
return keyPair != null;
}
/**
* @param signMessage
* Set to true if the message should be signed. For protecting an
* entry, this needs to be set to true.
* @return This class
*/
public SendDirectBuilder sign(final boolean signMessage) {
if (signMessage) {
sign();
} else {
this.keyPair = null;
}
return this;
}
/**
* @return Set to true if the message should be signed. For protecting an
* entry, this needs to be set to true.
*/
public SendDirectBuilder sign() {
this.keyPair = peer.peerBean().keyPair();
return this;
}
/**
* @param keyPair
* The keyPair to sing the complete message. The key will be
* attached to the message and stored potentially with a data
* object (if there is such an object in the message).
* @return This class
*/
public SendDirectBuilder keyPair(KeyPair keyPair) {
this.keyPair = keyPair;
return this;
}
/**
* @return The current keypair to sign the message. If null, no signature is
* applied.
*/
public KeyPair keyPair() {
return keyPair;
}
}