/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package staticContent.framework.socket.socketInterfaces;
import java.util.Random;
import staticContent.framework.AnonNode;
import staticContent.framework.config.Settings;
import staticContent.framework.controller.Layer1NetworkClientController;
import staticContent.framework.controller.Layer2RecodingSchemeClientController;
import staticContent.framework.controller.Layer3OutputStrategyClientController;
import staticContent.framework.controller.Layer4TransportClientController;
import staticContent.framework.interfaces.Layer1NetworkClient;
import staticContent.framework.interfaces.Layer2RecodingSchemeClient;
import staticContent.framework.interfaces.Layer3OutputStrategyClient;
import staticContent.framework.interfaces.Layer4TransportClient;
import staticContent.framework.interfaces.ThreePhaseStart;
import staticContent.framework.message.Request;
import staticContent.framework.routing.RoutingMode;
//TODO: ioexception instead of throw new RuntimeException(where possible);
//TODO: extends implementation ? (layer5clientimpl)
public abstract class AdaptiveAnonSocket implements AnonSocket, AnonSocketOptions, AnonSocketAddressData {
protected final static int NOT_SET = -1;
protected int endToEndPseudonym = NOT_SET;
protected int sourcePseudonym = NOT_SET;
protected int sourcePort = NOT_SET;
protected int destinationPseudonym = NOT_SET;
protected int destinationPort = NOT_SET;
protected boolean isDuplex = false;
protected CommunicationDirection communicationMode = null;
protected boolean isConnectionBased = false;
protected boolean isReliable = false;
protected boolean isOrderPreserving = false;
protected boolean isFreeRoute = false;
protected boolean isClientSideSocket;
protected Layer1NetworkClient layer1;
protected Layer2RecodingSchemeClient layer2;
protected Layer3OutputStrategyClient layer3;
protected Layer4TransportClient layer4;
protected AnonNode owner;
protected Settings settings;
//protected ArrayBlockingQueue<AnonMessage> receivedDatagrams = null;
private ConcurrentCapacityAwareMixMessageQueue<Request> receivedRequests = null;
// client-side
public AdaptiveAnonSocket(
AnonNode owner,
CommunicationDirection communicationMode,
boolean isConnectionBased,
boolean isReliable,
boolean isOrderPreserving,
boolean isFreeRoute
) {
this.isClientSideSocket = true;
init(owner, communicationMode, isConnectionBased, isReliable, isOrderPreserving, isFreeRoute);
if (!owner.LAYER_1_LINKS_MESSAGES) {
this.endToEndPseudonym = new Random().nextInt(); // TODO
this.sourcePseudonym = endToEndPseudonym;
this.sourcePort = endToEndPseudonym;
}
Layer1NetworkClientController layer1controller = owner.getNetworkLayerControllerClient();
this.layer1 = layer1controller.loadClientPluginInstance();
Layer2RecodingSchemeClientController layer2controller = owner.getRecodingLayerControllerClient();
this.layer2 = layer2controller.loadClientPluginInstance();
Layer3OutputStrategyClientController layer3controller = owner.getOutputStrategyLayerControllerClient();
this.layer3 = layer3controller.loadClientPluginInstance();
Layer4TransportClientController layer4controller = owner.getTransportLayerControllerClient();
this.layer4 = layer4controller.loadClientPluginInstance();
// three phase start:
this.layer1.setReferences(layer1, layer2, layer3, layer4);
this.layer2.setReferences(layer1, layer2, layer3, layer4);
this.layer3.setReferences(layer1, layer2, layer3, layer4);
this.layer4.setReferences(layer1, layer2, layer3, layer4);
((ThreePhaseStart)this.layer1).constructor();
((ThreePhaseStart)this.layer2).constructor();
((ThreePhaseStart)this.layer3).constructor();
((ThreePhaseStart)this.layer4).constructor();
((ThreePhaseStart)this.layer1).initialize();
((ThreePhaseStart)this.layer2).initialize();
((ThreePhaseStart)this.layer3).initialize();
((ThreePhaseStart)this.layer4).initialize();
((ThreePhaseStart)this.layer1).begin();
((ThreePhaseStart)this.layer2).begin();
((ThreePhaseStart)this.layer3).begin();
((ThreePhaseStart)this.layer4).begin();
}
// server/mix-side
public AdaptiveAnonSocket(
AnonNode owner,
int endToEndPseudonym,
CommunicationDirection communicationMode,
boolean isConnectionBased,
boolean isReliable,
boolean isOrderPreserving,
boolean isFreeRoute
) {
if (!owner.IS_MIX)
throw new RuntimeException("this constructor is only for mixes");
this.isClientSideSocket = false;
init(owner, communicationMode, isConnectionBased, isReliable, isOrderPreserving, isFreeRoute);
this.endToEndPseudonym = endToEndPseudonym;
this.destinationPseudonym = endToEndPseudonym;
this.sourcePseudonym = endToEndPseudonym;
//this.receivedDatagrams = new ArrayBlockingQueue<AnonMessage>(owner.SOCKET_MIX_BACKEND_QUEUE_SIZE);
this.receivedRequests = new ConcurrentCapacityAwareMixMessageQueue<Request>(owner.SOCKET_MIX_BACKEND_QUEUE_SIZE);
}
private void init(
AnonNode owner,
CommunicationDirection communicationMode,
boolean isConnectionBased,
boolean isReliable,
boolean isOrderPreserving,
boolean isFreeRoute
) {
if (communicationMode == CommunicationDirection.DUPLEX && !owner.IS_DUPLEX)
throw new RuntimeException("the current plug-in config does not support duplex sockets");
if (isConnectionBased && !owner.IS_CONNECTION_BASED)
throw new RuntimeException("the current plug-in config does not support connection-based sockets");
if (isReliable && !owner.IS_RELIABLE)
throw new RuntimeException("the current plug-in config does not support reliable transfer");
if (isOrderPreserving && !owner.IS_ORDER_PRESERVING)
throw new RuntimeException("the current plug-in config does not support order-preserving transfer");
if (isFreeRoute && owner.ROUTING_MODE == RoutingMode.GLOBAL_ROUTING)
throw new RuntimeException("the current plug-in config does not support free routes transfer");
if (!isFreeRoute && owner.ROUTING_MODE != RoutingMode.GLOBAL_ROUTING)
throw new RuntimeException("the current plug-in config does not support cascades");
this.owner = owner;
this.settings = owner.getSettings();
this.communicationMode = communicationMode;
this.isConnectionBased = isConnectionBased;
this.isReliable = isReliable;
this.isOrderPreserving = isOrderPreserving;
this.isFreeRoute = isFreeRoute;
if (communicationMode == CommunicationDirection.DUPLEX)
this.isDuplex = true;
}
public String toString() {
String s = "";
s += "endToEndPseudonym: " +endToEndPseudonym +"\n";
s += "sourcePseudonym: " +sourcePseudonym +"\n";
s += "sourcePort: " +sourcePort +"\n";
s += "destinationPseudonym: " +destinationPseudonym +"\n";
s += "destinationPort: " +destinationPort +"\n";
s += "communicationMode: " +communicationMode +"\n";
s += "isReliable: " +isReliable +"\n";
s += "isOrderPreserving: " +isOrderPreserving +"\n";
s += "isFreeRoute: " +isOrderPreserving +"\n";
s += "isFreeRoute: " +isFreeRoute +"\n";
return s;
}
public void newIncomingMessage(Request message) {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
try {
receivedRequests.add(message);
} catch (InterruptedException e) {
e.printStackTrace();
newIncomingMessage(message);
}
}
public Request getNextRequest() {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
try {
return receivedRequests.get();
} catch (InterruptedException e) {
e.printStackTrace();
return getNextRequest();
}
}
// returns a request only if its size is smaller than maxSize
public Request getNextRequest(int maxSize) {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
try {
Request nextRequest = receivedRequests.peek();
if (nextRequest == null || nextRequest.getByteMessage().length > maxSize)
return null;
else
return receivedRequests.get();
} catch (InterruptedException e) {
e.printStackTrace();
return getNextRequest();
}
}
public int availableRequests() {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
return receivedRequests.messagesAvailable();
}
public int sizeOfNextRequest() {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
Request next = receivedRequests.peek();
if (next == null)
return 0;
else
return next.getByteMessage().length;
}
public int availableData() {
if (isClientSideSocket)
throw new RuntimeException("this method is only available on server-side plugins");
return receivedRequests.bytesAvailable();
}
// AnonSocketOptions
@Override
public boolean getIsConnectionBased() {
return this.isConnectionBased;
}
// AnonSocketOptions
@Override
public boolean getIsReliable() {
return this.isReliable;
}
// AnonSocketOptions
@Override
public boolean getIsOrderPreserving() {
return this.isOrderPreserving;
}
// AnonSocketOptions
@Override
public boolean getIsFreeRouteSocket() {
return this.isFreeRoute;
}
// AnonSocketOptions
@Override
public boolean getIsDuplex() {
return this.isDuplex;
}
// AnonSocketOptions
@Override
public CommunicationDirection getCommunicationDirection() {
return this.communicationMode;
}
// AnonSocketAddressData
@Override
public int getDestinationPort() {
return this.destinationPort;
}
// AnonSocketAddressData
@Override
public AnonNode getOwner() {
return this.owner;
}
// AnonSocketAddressData
@Override
public int getEndToEndPseudonym() {
assert endToEndPseudonym != NOT_SET;
return this.endToEndPseudonym;
}
// AnonSocketAddressData
@Override
public int getSourcePseudonym() {
return this.sourcePseudonym;
}
// AnonSocketAddressData
@Override
public int getSourcePort() {
return this.sourcePort;
}
// AnonSocketAddressData
@Override
public int getDestinationPseudonym() {
return this.destinationPseudonym;
}
}